From 0890328ce70d6e88c2331e03bf1ebc16e9557254 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Sat, 24 Oct 2026 17:01:58 -0500 Subject: [PATCH] [java] enforce warning-free builds Java quality checks succeed while repeating intentional test-resource warnings and shaded archive warnings on every PR. Keep `-Xlint:all`, suppress only the test methods that hold workers or subscriptions open for their lifetime, and reject unsuppressed compiler warnings. Merge dependency license and notice text in both executable archives, generate one manifest, and exclude dependency module descriptors and signatures. Preserve multi-release dependency classes with the matching manifest entry. Run Maven through a shared Make wrapper that streams its output and rejects plugin warnings while preserving command failures. Cover warning output on both streams, ordinary output, and failing command exit codes with focused regression checks. Record the language-prefix convention for PR titles and commit subjects so port-specific changes identify their scope without repeating it. --- AGENTS.md | 7 +++++ Makefile | 16 +++++----- java/bin/check-maven-warnings.sh | 14 +++++++++ java/bin/check-maven-warnings_test.sh | 31 +++++++++++++++++++ java/cli/pom.xml | 8 +++-- java/conformance/pom.xml | 25 ++++++++++++++- java/docs/development.md | 19 ++++++++++++ java/pom.xml | 10 +++++- .../test/java/com/riverqueue/ClientTest.java | 5 +++ .../com/riverqueue/HookMiddlewareTest.java | 2 ++ .../riverqueue/NotificationCleanupTest.java | 1 + .../java/com/riverqueue/NotificationTest.java | 1 + .../com/riverqueue/PeerCancellationTest.java | 1 + .../test/java/com/riverqueue/PluginTest.java | 1 + .../com/riverqueue/ProtocolStorageTest.java | 2 ++ .../test/java/com/riverqueue/RescueTest.java | 3 ++ .../java/com/riverqueue/ResumableTest.java | 3 ++ .../test/java/com/riverqueue/RetryTest.java | 1 + .../java/com/riverqueue/SchedulingTest.java | 5 +++ .../test/java/com/riverqueue/TimeoutTest.java | 1 + .../com/riverqueue/VirtualThreadTest.java | 1 + .../test/java/com/riverqueue/WorkersTest.java | 16 ++++++++++ 22 files changed, 162 insertions(+), 11 deletions(-) create mode 100755 java/bin/check-maven-warnings.sh create mode 100644 java/bin/check-maven-warnings_test.sh diff --git a/AGENTS.md b/AGENTS.md index 4a22e7787..6c017ca48 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,13 @@ This repo holds the Go reference implementation and its ports (`java/`, `js/`, ` - **Same tests everywhere**: give each affected implementation an equivalent test for the behavior, on every database it supports. - **Document intended differences**: an API should be idiomatic for its language, but observable behavior should match Go. Record any difference that remains in the port's differences doc (for example `java/DIFFERENCES.md`). +## Commit and PR Titles + +- When a change affects only a subset of the languages, prefix the PR title and commit subjects with the affected languages in lowercase brackets: `[java]`, `[go]`, or `[go, ruby]`. +- Use `go`, `java`, `js`, `ruby`, and `rust` as the language names. List multiple languages alphabetically, separated by a comma and a space. +- Avoid repeating the language names in the rest of the title when the prefix already makes the scope clear. For example, use `[java] Enforce warning-free builds` instead of `[java] Enforce warning-free Java builds`. +- Omit the prefix for changes that affect all languages or are independent of any particular language, such as repository-wide contributor guidance. + ## Package Naming and Organization - Package names are lowercase, short, and representative; avoid `common`, `util`, or overly broad names. diff --git a/Makefile b/Makefile index 867bdf544..b4f16f969 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ SQLC ?= sqlc MVN ?= mvn +java_mvn = ./java/bin/check-maven-warnings.sh $(MVN) --batch-mode --no-transfer-progress -Dstyle.color=never .PHONY: check/modzip check/modzip: ## Check that no Go module zip includes fixtures, testdata, or another language's port @@ -117,12 +118,12 @@ lint/rust: ## Run Rust formatting and clippy checks, including single-backend bu # Java targets stay separate so Go-only contributors do not need a JDK or Maven. .PHONY: build/java build/java: ## Build the Java library, CLI, and development adapter (JDK 21+) - $(MVN) --batch-mode --no-transfer-progress -f java/pom.xml package -DskipTests + $(java_mvn) -f java/pom.xml package -DskipTests .PHONY: lint/java lint/java: ## Compile Java, check formatting, and lint Go maintenance tools lint/java: lint/java/tools - $(MVN) --batch-mode --no-transfer-progress -f java/pom.xml verify -DskipTests + $(java_mvn) -f java/pom.xml verify -DskipTests # Java's module boundary excludes it from Go package discovery. Pass the tool # files explicitly so they use this workspace's Go version and test dependencies. @@ -172,27 +173,28 @@ RUST_POSTGRES_TESTS_ENV = RUSTFLAGS="$$RUSTFLAGS --cfg river_postgres_tests" \ test/java: ## Run Java tests, executable CLI tests, and formatting checks test/java: generate/fixtures verify/java-migrations test/java: test/java/tools - $(MVN) --batch-mode --no-transfer-progress -f java/pom.xml verify + $(java_mvn) -f java/pom.xml verify # Fixture comparisons use temporary SQLite databases, with no Postgres or legacy adapter. .PHONY: test/java/conformance test/java/conformance: ## Run Java tests that check Go-generated conformance fixtures test/java/conformance: generate/fixtures - $(MVN) --batch-mode --no-transfer-progress -f java/pom.xml -pl river test -Dgroups=conformance -Driver.test.database=sqlite + $(java_mvn) -f java/pom.xml -pl river test -Dgroups=conformance -Driver.test.database=sqlite .PHONY: test/java/postgres test/java/postgres: ## Run Java tests with Postgres client and worker coverage (requires RIVER_TEST_DATABASE_URL) test/java/postgres: generate/fixtures verify/java-migrations @test -n "$$RIVER_TEST_DATABASE_URL" || { echo "RIVER_TEST_DATABASE_URL is required" >&2; exit 1; } - $(MVN) --batch-mode --no-transfer-progress -f java/pom.xml verify -Driver.test.database=postgres + $(java_mvn) -f java/pom.xml verify -Driver.test.database=postgres .PHONY: test/java/sqlite test/java/sqlite: ## Run Java unit, SQLite, and executable CLI tests without Postgres test/java/sqlite: generate/fixtures verify/java-migrations - $(MVN) --batch-mode --no-transfer-progress -f java/pom.xml verify -Driver.test.database=sqlite -DexcludedGroups=postgres + $(java_mvn) -f java/pom.xml verify -Driver.test.database=sqlite -DexcludedGroups=postgres .PHONY: test/java/tools -test/java/tools: ## Test Java's Go maintenance tools +test/java/tools: ## Test Java maintenance tools and Maven warning enforcement + bash java/bin/check-maven-warnings_test.sh go test ./java/bin/check-packages/*.go go test ./java/bin/sync-migrations/*.go diff --git a/java/bin/check-maven-warnings.sh b/java/bin/check-maven-warnings.sh new file mode 100755 index 000000000..3bc2253fd --- /dev/null +++ b/java/bin/check-maven-warnings.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Maven 3 has no general fail-on-warning option. Keep its output visible, then +# reject warnings from packaging and other plugins as well as the compiler. +set -euo pipefail + +maven_log=$(mktemp) +trap 'rm -f "$maven_log"' EXIT + +"$@" 2>&1 | tee "$maven_log" + +if grep -q '^\[WARNING\]' "$maven_log"; then + echo 'Java build emitted Maven warnings; fix them or document an intentional suppression.' >&2 + exit 1 +fi diff --git a/java/bin/check-maven-warnings_test.sh b/java/bin/check-maven-warnings_test.sh new file mode 100644 index 000000000..bb60c985d --- /dev/null +++ b/java/bin/check-maven-warnings_test.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +test_dir=$(mktemp -d) +trap 'rm -rf "$test_dir"' EXIT + +check() { + local name=$1 expected=$2 output=$3 message=$4 + local actual=0 + "$script_dir/check-maven-warnings.sh" bash -c "$output" >"$test_dir/output" 2>&1 || actual=$? + if [[ "$actual" -ne "$expected" ]]; then + cat "$test_dir/output" >&2 + echo "$name: expected exit $expected, got $actual" >&2 + exit 1 + fi + if ! grep -Fq "$message" "$test_dir/output"; then + echo "$name: missing command output: $message" >&2 + exit 1 + fi +} + +check clean 0 'echo "[INFO] BUILD SUCCESS"' '[INFO] BUILD SUCCESS' +check warning_stdout 1 'echo "[WARNING] duplicate class"' '[WARNING] duplicate class' +check warning_stderr 1 'echo "[WARNING] duplicate resource" >&2' '[WARNING] duplicate resource' +check command_failure 23 'echo "[ERROR] build failed"; exit 23' '[ERROR] build failed' +check failure_with_warning 17 'echo "[WARNING] warning before failure"; exit 17' '[WARNING] warning before failure' +check ordinary_word 0 'echo "[INFO] test checks WARNING handling"' '[INFO] test checks WARNING handling' +check application_log 0 'echo "WARNING: expected test log"' 'WARNING: expected test log' + +echo 'Maven warning enforcement checks passed' diff --git a/java/cli/pom.xml b/java/cli/pom.xml index 76e14a5ea..3746eb427 100644 --- a/java/cli/pom.xml +++ b/java/cli/pom.xml @@ -19,13 +19,17 @@ false trueall + + META-INF/LICENSE + META-INF/NOTICE com.riverqueue.cli.MigrationCli - ${project.version}ALL-UNNAMED + true${project.version}ALL-UNNAMED - *:*META-INF/*.SFMETA-INF/*.DSAMETA-INF/*.RSAmodule-info.classMETA-INF/versions/*/module-info.class + + *:*META-INF/MANIFEST.MFMETA-INF/*.SFMETA-INF/*.DSAMETA-INF/*.RSAmodule-info.classMETA-INF/versions/*/module-info.class org.apache.maven.pluginsmaven-failsafe-plugin3.5.4 diff --git a/java/conformance/pom.xml b/java/conformance/pom.xml index c03b1ac56..1e300d437 100644 --- a/java/conformance/pom.xml +++ b/java/conformance/pom.xml @@ -10,5 +10,28 @@ org.postgresqlpostgresql org.xerialsqlite-jdbc - org.apache.maven.pluginsmaven-shade-plugin3.6.1packageshadefalsecom.riverqueue.conformance.Adapter*:*META-INF/*.SFMETA-INF/*.DSAMETA-INF/*.RSA + + + org.apache.maven.pluginsmaven-shade-plugin3.6.1 + packageshade + false + + + META-INF/LICENSE + META-INF/NOTICE + + + com.riverqueue.conformance.Adapter + true + + + + *:* + META-INF/MANIFEST.MF + META-INF/*.SFMETA-INF/*.DSAMETA-INF/*.RSA + module-info.classMETA-INF/versions/*/module-info.class + + + + diff --git a/java/docs/development.md b/java/docs/development.md index 3c55228f2..eb8f58977 100644 --- a/java/docs/development.md +++ b/java/docs/development.md @@ -82,6 +82,25 @@ filtered to changes in Java, its build configuration, fixtures, and canonical migrations. Go changes run the smaller fixture suite in the shared Conformance workflow alongside Rust and JavaScript. +## Warning policy + +Java builds keep `-Xlint:all` enabled and fail on unsuppressed compiler warnings, +including test compilation. Tests that intentionally hold workers or subscriptions +open for a block use a method-level `@SuppressWarnings("try")` with a short reason. +Other compiler diagnostics remain enabled. + +The Make targets run Maven through `bin/check-maven-warnings.sh`. It streams the +complete build output, preserves command failures, and rejects Maven warning +messages from packaging and other plugins. Local checks and CI use these same +targets. Direct Maven invocations also reject compiler warnings through the +parent POM's `failOnWarning` setting. + +Executable JARs merge dependency `META-INF/LICENSE` and `META-INF/NOTICE` text, +retain service registrations, and generate their own executable manifests. +Dependency module descriptors and signatures are excluded from these combined +archives. Other overlapping classes or resources still produce warnings and fail +the checked Make targets. Library and source JARs keep their normal packaging. + ## Legacy cross-process harness The original interoperability adapter remains available for broader storage, diff --git a/java/pom.xml b/java/pom.xml index b858716ae..94aef9a28 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -20,7 +20,15 @@ org.apache.maven.pluginsmaven-jar-plugin3.5.1true - org.apache.maven.pluginsmaven-compiler-plugin3.14.1truetrue-Xlint:all + + org.apache.maven.pluginsmaven-compiler-plugin3.14.1 + + true + true + true + -Xlint:all + + org.apache.maven.pluginsmaven-surefire-plugin3.5.4--enable-native-access=ALL-UNNAMED org.apache.maven.pluginsmaven-source-plugin3.3.1jar-no-fork com.diffplug.spotlessspotless-maven-plugin2.44.31.28.0verifycheck diff --git a/java/river/src/test/java/com/riverqueue/ClientTest.java b/java/river/src/test/java/com/riverqueue/ClientTest.java index aed0cd68b..dfb0d6e90 100644 --- a/java/river/src/test/java/com/riverqueue/ClientTest.java +++ b/java/river/src/test/java/com/riverqueue/ClientTest.java @@ -288,6 +288,7 @@ public void insertBegin( } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void fetchOnlyKnownKindsLeavesPeerJobsAvailable() throws Exception { var river = database(); var peer = JobType.of("peer_only", Email.class); @@ -523,6 +524,7 @@ public void insertBegin( } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void sqliteQueueNotificationFailureRollsBackQueueMutation() throws Exception { var river = new Client( @@ -555,6 +557,7 @@ void sqliteQueueNotificationFailureRollsBackQueueMutation() throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void committedCompletionWinsOverSubsequentHandlerFailure() throws Exception { var river = database(); var event = new CompletableFuture(); @@ -585,6 +588,7 @@ void committedCompletionWinsOverSubsequentHandlerFailure() throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void retryPolicyReceivesFailedAttemptSnapshot() throws Exception { var river = database(); var observed = new CompletableFuture>(); @@ -623,6 +627,7 @@ void retryPolicyReceivesFailedAttemptSnapshot() throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void jobKindLengthMatchesDatabaseConstraint() throws Exception { var client = database(); var type = JobType.of("k".repeat(127), Email.class); diff --git a/java/river/src/test/java/com/riverqueue/HookMiddlewareTest.java b/java/river/src/test/java/com/riverqueue/HookMiddlewareTest.java index 727131840..cb47eda4b 100644 --- a/java/river/src/test/java/com/riverqueue/HookMiddlewareTest.java +++ b/java/river/src/test/java/com/riverqueue/HookMiddlewareTest.java @@ -176,6 +176,7 @@ void registrationRejectsNullEntries() { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void workBeginFailureSkipsHandlerAndEndHooks() throws Exception { var trace = new ArrayList(); var river = @@ -222,6 +223,7 @@ void workBeginFailureSkipsHandlerAndEndHooks() throws Exception { "failure,replace", "success,throw" }) + @SuppressWarnings("try") // Resources intentionally scope background activity. void workEndHooksChainOutcomesInsideMiddleware(String outcome, String action) throws Exception { var trace = new ArrayList(); var failure = new IOException("handler failed"); diff --git a/java/river/src/test/java/com/riverqueue/NotificationCleanupTest.java b/java/river/src/test/java/com/riverqueue/NotificationCleanupTest.java index 002a79db0..d7bc09a6a 100644 --- a/java/river/src/test/java/com/riverqueue/NotificationCleanupTest.java +++ b/java/river/src/test/java/com/riverqueue/NotificationCleanupTest.java @@ -54,6 +54,7 @@ void deletesOldestRowsInBoundedBatchesAndRetainsCutoff() { @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void leaderCleansNotificationsEvenWhenPluginOwnsJobCleaning(boolean pluginOwnsCleaning) throws Exception { var database = TestDatabase.sqlite(directory.resolve("river.db")); diff --git a/java/river/src/test/java/com/riverqueue/NotificationTest.java b/java/river/src/test/java/com/riverqueue/NotificationTest.java index 4e443cb92..a4718fc9f 100644 --- a/java/river/src/test/java/com/riverqueue/NotificationTest.java +++ b/java/river/src/test/java/com/riverqueue/NotificationTest.java @@ -21,6 +21,7 @@ class NotificationTest { @TempDir Path directory; @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void sqliteListenerRecoversFromAnInitialDatabaseFailure() throws Exception { var database = TestDatabase.sqlite(directory.resolve("river.db")); var fail = new AtomicBoolean(true); diff --git a/java/river/src/test/java/com/riverqueue/PeerCancellationTest.java b/java/river/src/test/java/com/riverqueue/PeerCancellationTest.java index f2d5c590d..b0ef87333 100644 --- a/java/river/src/test/java/com/riverqueue/PeerCancellationTest.java +++ b/java/river/src/test/java/com/riverqueue/PeerCancellationTest.java @@ -138,6 +138,7 @@ else if (cause == WorkContext.Cancellation.SHUTDOWN) { @ParameterizedTest @ValueSource(strings = {"decode", "work"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void peerTimeoutInterruptsTheOwningWorker(String phase) throws Exception { var entered = new CompletableFuture(); var interrupted = new CompletableFuture(); diff --git a/java/river/src/test/java/com/riverqueue/PluginTest.java b/java/river/src/test/java/com/riverqueue/PluginTest.java index 0c4396744..e5d4dce97 100644 --- a/java/river/src/test/java/com/riverqueue/PluginTest.java +++ b/java/river/src/test/java/com/riverqueue/PluginTest.java @@ -94,6 +94,7 @@ void pluginRegistrationRetainsDuplicates() { @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void pluginsActivateAllJobInterfacesBeforeExplicitRegistrations(boolean pluginsFirst) throws Exception { var trace = new CopyOnWriteArrayList(); diff --git a/java/river/src/test/java/com/riverqueue/ProtocolStorageTest.java b/java/river/src/test/java/com/riverqueue/ProtocolStorageTest.java index b5e3e0da1..47fdbc6fe 100644 --- a/java/river/src/test/java/com/riverqueue/ProtocolStorageTest.java +++ b/java/river/src/test/java/com/riverqueue/ProtocolStorageTest.java @@ -27,6 +27,7 @@ private Client client() { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void periodicMetadata() throws Exception { var keys = Conformance.fixture("protocol_values.json").required("metadata_keys"); var client = client(); @@ -59,6 +60,7 @@ void periodicMetadata() throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void rescueCountAndOutput() throws Exception { var fixture = Conformance.fixture("protocol_values.json"); var keys = fixture.required("metadata_keys"); diff --git a/java/river/src/test/java/com/riverqueue/RescueTest.java b/java/river/src/test/java/com/riverqueue/RescueTest.java index 26530af95..549d5448f 100644 --- a/java/river/src/test/java/com/riverqueue/RescueTest.java +++ b/java/river/src/test/java/com/riverqueue/RescueTest.java @@ -28,6 +28,7 @@ class RescueTest { @ParameterizedTest @ValueSource(strings = {"default", "disabled", "error", "override", "throw", "zero"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void pilotReceivesWorkerTimeoutCallback(String mode) throws Exception { var request = new CompletableFuture(); var errors = new LinkedBlockingQueue(); @@ -198,6 +199,7 @@ public void work(WorkContext context) { @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void rescueHonorsWorkerTimeouts(boolean defaultDisabled) throws Exception { var maintained = new CompletableFuture(); var errors = new LinkedBlockingQueue(); @@ -293,6 +295,7 @@ public void work(WorkContext context) { @ParameterizedTest @ValueSource(strings = {"local", "peer", "peer_claim_retry", "peer_notification_retry"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void shortRescuedRetriesWakeFetchingBeforeMaintenance(String mode) throws Exception { boolean peer = !mode.equals("local"); var database = databases.open(directory.resolve("river.db")); diff --git a/java/river/src/test/java/com/riverqueue/ResumableTest.java b/java/river/src/test/java/com/riverqueue/ResumableTest.java index f8106a545..08349069a 100644 --- a/java/river/src/test/java/com/riverqueue/ResumableTest.java +++ b/java/river/src/test/java/com/riverqueue/ResumableTest.java @@ -32,6 +32,7 @@ class ResumableTest { "true,true,false", "true,true,true" }) + @SuppressWarnings("try") // Resources intentionally scope background activity. void checkpointCommitsWithApplicationWrites( boolean priorCheckpoint, boolean rollback, boolean withCursor) throws Exception { var client = new Client(databases.open(directory.resolve("river.db"))); @@ -238,6 +239,7 @@ void checkpointRetainsOnlyCommittedProgress(boolean rollback, boolean finishStep @ParameterizedTest @ValueSource(strings = {"plain", "cursor", "null", "first", "first_null"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void cursorDoesNotCompleteFailedSteps(String mode) throws Exception { var client = new Client(databases.open(directory.resolve("river.db"))); var events = new LinkedBlockingQueue(); @@ -327,6 +329,7 @@ void cursorInFirstStepIsBufferedAndNestedStepsAreRejected() throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void cursorMapsReplaceCompletedStepsAndPreserveNullValues() throws Exception { var client = new Client(databases.open(directory.resolve("river.db"))); var events = new LinkedBlockingQueue(); diff --git a/java/river/src/test/java/com/riverqueue/RetryTest.java b/java/river/src/test/java/com/riverqueue/RetryTest.java index 52385d523..30f9bdd73 100644 --- a/java/river/src/test/java/com/riverqueue/RetryTest.java +++ b/java/river/src/test/java/com/riverqueue/RetryTest.java @@ -69,6 +69,7 @@ void retryNotifiesOnlyWhenTheRowChanges(Job.State state) { @ParameterizedTest @ValueSource(strings = {"local", "peer", "transaction"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void retryWakesWorkersAfterCommit(String mode) throws Exception { var database = databases.open(directory.resolve("river.db")); var warmed = new CompletableFuture(); diff --git a/java/river/src/test/java/com/riverqueue/SchedulingTest.java b/java/river/src/test/java/com/riverqueue/SchedulingTest.java index 0d3e565da..d2e752ef6 100644 --- a/java/river/src/test/java/com/riverqueue/SchedulingTest.java +++ b/java/river/src/test/java/com/riverqueue/SchedulingTest.java @@ -31,6 +31,7 @@ class SchedulingTest { @TempDir Path directory; @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void anonymousPeriodicJobsOmitTheIdMetadata() throws Exception { var client = new Client(databases.open(directory.resolve("river.db"))); var worked = new CompletableFuture>(); @@ -228,6 +229,7 @@ public void maintain(Client client, Instant now) { @ParameterizedTest @ValueSource(strings = {"initial schedule", "following schedule", "insertion"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void failedPeriodicJobDoesNotBlockMaintenanceOrOtherJobs(String failurePoint) throws Exception { var errors = new LinkedBlockingQueue(); var worked = new LinkedBlockingQueue(); @@ -285,6 +287,7 @@ public void afterInsert( } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void failedScheduleDoesNotInsertTheOccurrenceTwice() throws Exception { var calls = new AtomicInteger(); var maintained = new CompletableFuture(); @@ -330,6 +333,7 @@ public void maintain(Client client, Instant now) { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void periodicSchedulesKeepTheirPlannedCadence() throws Exception { var inserted = new AtomicReference>(); var client = @@ -377,6 +381,7 @@ public void afterInsert( @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void periodicSchedulesUseTheSystemTimeZone(boolean runOnStart) throws Exception { var original = TimeZone.getDefault(); var zone = ZoneId.of("Asia/Kolkata"); diff --git a/java/river/src/test/java/com/riverqueue/TimeoutTest.java b/java/river/src/test/java/com/riverqueue/TimeoutTest.java index 76ed0815f..0cd4ac3c3 100644 --- a/java/river/src/test/java/com/riverqueue/TimeoutTest.java +++ b/java/river/src/test/java/com/riverqueue/TimeoutTest.java @@ -20,6 +20,7 @@ class TimeoutTest { @ParameterizedTest @ValueSource(strings = {"inherit", "override", "disabled", "longer", "zero", "overflow", "throw"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void handlerTimeoutControlsTheAttempt(String mode) throws Exception { var client = new Client(databases.open(directory.resolve("river.db"))); var calls = new AtomicInteger(); diff --git a/java/river/src/test/java/com/riverqueue/VirtualThreadTest.java b/java/river/src/test/java/com/riverqueue/VirtualThreadTest.java index 081488b3f..dfe2d8aed 100644 --- a/java/river/src/test/java/com/riverqueue/VirtualThreadTest.java +++ b/java/river/src/test/java/com/riverqueue/VirtualThreadTest.java @@ -61,6 +61,7 @@ private void probe(String scenario) throws Exception { } public static final class Probe { + @SuppressWarnings("try") // Resources intentionally scope background activity. public static void main(String[] args) throws Exception { var entered = new CountDownLatch(1); var release = new CountDownLatch(1); diff --git a/java/river/src/test/java/com/riverqueue/WorkersTest.java b/java/river/src/test/java/com/riverqueue/WorkersTest.java index b75176dd9..2d54db34b 100644 --- a/java/river/src/test/java/com/riverqueue/WorkersTest.java +++ b/java/river/src/test/java/com/riverqueue/WorkersTest.java @@ -73,6 +73,7 @@ void builderRequiresServiceIntervalsBelowQueueRetention(String value, boolean va @ParameterizedTest @CsvSource({"REMOTE,false", "REMOTE,true", "TIMEOUT,false", "TIMEOUT,true"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void cancellationRespectsTheHandlersOutcome(WorkContext.Cancellation cause, boolean interrupted) throws Exception { var client = client(); @@ -119,6 +120,7 @@ void cancellationRespectsTheHandlersOutcome(WorkContext.Cancellation cause, bool @EnumSource( value = Job.State.class, names = {"AVAILABLE", "PENDING", "RETRYABLE", "RUNNING", "SCHEDULED"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void completionEventsReflectPersistedState(Job.State state) throws Exception { var client = client(); var database = client.database(); @@ -176,6 +178,7 @@ void completionEventsReflectPersistedState(Job.State state) throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void completionRetriesSurviveAnInterruptedHandler() throws Exception { var attempts = new AtomicInteger(); var client = @@ -215,6 +218,7 @@ public void afterAttempt( @ParameterizedTest @ValueSource(strings = {"args", "kind", "row"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void completionRetriesSurviveInvalidJobs(String invalid) throws Exception { var attempts = new AtomicInteger(); var client = @@ -288,6 +292,7 @@ public java.util.List claim( } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void completedHandlerIsNotCancelledWhileAcknowledging() throws Exception { var acknowledging = new CountDownLatch(1); var release = new CountDownLatch(1); @@ -324,6 +329,7 @@ public void afterAttempt( } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void jobTimeoutDoesNotDependOnTheDispatcher() throws Exception { var blocked = new CountDownLatch(1); var release = new CountDownLatch(1); @@ -405,6 +411,7 @@ void oversizedDurationsFailDuringConfiguration(String option) { @ParameterizedTest @ValueSource(strings = {"error", "shutdown", "snooze"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void firstStepCursorSurvivesIncompleteAttempt(String outcome) throws Exception { var client = client(); var entered = new CountDownLatch(1); @@ -442,6 +449,7 @@ void firstStepCursorSurvivesIncompleteAttempt(String outcome) throws Exception { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void oversizedSnoozeFailsTheAttemptInsteadOfStrandingIt() throws Exception { var client = client(); var failed = new CompletableFuture(); @@ -517,6 +525,7 @@ void rescueCannotPreemptAConfiguredTimeout() { @ParameterizedTest @ValueSource(ints = {-1, 0, 120}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void rescueDefaultAccountsForTheJobTimeout(int timeoutMinutes) throws Exception { var rescue = new CompletableFuture(); var client = @@ -540,6 +549,7 @@ public boolean rescue(Client client, Rescue request) { } @Test + @SuppressWarnings("try") // Resources intentionally scope background activity. void goQueueNotificationsRespectTopicsAndSurviveMalformedPayloads() throws Exception { var client = client(); var ready = new CountDownLatch(1); @@ -682,6 +692,7 @@ private static tools.jackson.databind.JsonNode notification(String name) throws @ParameterizedTest @ValueSource(strings = {"observer", "subscriber", "errorHandler"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void callbackFailuresDoNotLoseWorkerSlots(String callback) throws Exception { var client = client(); var completed = new LinkedBlockingQueue(); @@ -738,6 +749,7 @@ void callbackFailuresDoNotLoseWorkerSlots(String callback) throws Exception { "nanosecond_overflow", "timestamp_overflow" }) + @SuppressWarnings("try") // Resources intentionally scope background activity. void invalidRetryPoliciesFallBack(String policy) throws Exception { var client = client(); var failed = new LinkedBlockingQueue(); @@ -777,6 +789,7 @@ void invalidRetryPoliciesFallBack(String policy) throws Exception { @ParameterizedTest @CsvSource({"false,1,5000", "true,1,5000", "false,3600,1", "true,3600,1"}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void shortDelaysWakeFetchingBeforeThePollInterval( boolean snooze, long serviceSeconds, long maintenanceMillis) throws Exception { var client = client(); @@ -823,6 +836,7 @@ void shortDelaysWakeFetchingBeforeThePollInterval( @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void shutdownRespectsTheHandlersOutcome(boolean interrupted) throws Exception { var client = client(); var entered = new CompletableFuture>(); @@ -864,6 +878,7 @@ void shutdownRespectsTheHandlersOutcome(boolean interrupted) throws Exception { @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void stoppingDuringAClaimPreservesCancellation(boolean cancel) throws Exception { var claimed = new CountDownLatch(1); var commit = new CountDownLatch(1); @@ -932,6 +947,7 @@ public java.util.List claim( @ParameterizedTest @ValueSource(booleans = {false, true}) + @SuppressWarnings("try") // Resources intentionally scope background activity. void terminalFailuresDoNotConsultRetryPolicy(boolean cancel) throws Exception { var client = client(); var failed = new LinkedBlockingQueue();