diff --git a/.github/actions/run-gradle/action.yml b/.github/actions/run-gradle/action.yml index 85733e1a42..0b463ec204 100644 --- a/.github/actions/run-gradle/action.yml +++ b/.github/actions/run-gradle/action.yml @@ -8,7 +8,7 @@ inputs: required: true description: The JDK version early-access: - default: '24' + default: '25' required: false description: The early access release graal: diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 21b11a231d..2f56447956 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 11, 21, 24, GraalVM ] + java: [ 11, 21, 25, GraalVM ] env: JAVA_VERSION: ${{ matrix.java }} steps: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 603badb669..c8acf43346 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 11, 23, 24, GraalVM ] + java: [ 11, 23, 25, GraalVM ] env: JAVA_VERSION: ${{ matrix.java }} steps: diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 9a9c694176..b5b9b73ff9 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -47,7 +47,7 @@ jobs: if: steps.check_files.outputs.files_exists == 'true' run: jq -c '.runs |= unique_by({tool, invocations, results})' < results.sarif > codacy.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 if: steps.check_files.outputs.files_exists == 'true' continue-on-error: true with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4a8be9ca03..4eee51d21c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -57,10 +57,10 @@ jobs: java: ${{ env.JAVA_VERSION }} cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - name: Initialize CodeQL - uses: github/codeql-action/init@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 with: languages: java - name: Autobuild - uses: github/codeql-action/autobuild@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/autobuild@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index febc77e218..7fc6b38fec 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -61,7 +61,7 @@ jobs: with: files: build/reports/dependency-check-report.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 if: steps.check_files.outputs.files_exists == 'true' with: sarif_file: build/reports/dependency-check-report.sarif diff --git a/.github/workflows/devskim.yml b/.github/workflows/devskim.yml index 605bf17a47..8eadd521e0 100644 --- a/.github/workflows/devskim.yml +++ b/.github/workflows/devskim.yml @@ -31,6 +31,6 @@ jobs: - name: Run DevSkim scanner uses: microsoft/DevSkim-Action@914fa647b406c387000300b2f09bb28691be2b6d # v1.0.14 - name: Upload DevSkim scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 with: sarif_file: devskim-results.sarif diff --git a/.github/workflows/qodana.yml b/.github/workflows/qodana.yml index a045ce5342..e89214c512 100644 --- a/.github/workflows/qodana.yml +++ b/.github/workflows/qodana.yml @@ -63,13 +63,13 @@ jobs: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: check -x test - name: Qodana - Code Inspection - uses: JetBrains/qodana-action@31d6f3309b31c566758e1314a3d9ef0dff75ecbd # v2024.2.6 + uses: JetBrains/qodana-action@b46847499776819f38aad1d4298e16e52f17c4c5 # v2024.3.2 env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} with: upload-result: true github-token: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file for GitHub Advanced Security Dashboard - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 with: sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 05d9b9c6a0..f829b7fcda 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -58,6 +58,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 with: sarif_file: results.sarif diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 1ac3027da5..0effa0228a 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -34,7 +34,7 @@ jobs: if: steps.check_files.outputs.files_exists == 'true' run: jq -c '.runs[0].tool.driver.rules |= unique_by(.id)' < results.sarif > semgrep.sarif - name: Upload SARIF file for GitHub Advanced Security Dashboard - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 if: steps.check_files.outputs.files_exists == 'true' continue-on-error: true with: diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 4f2fd16e5c..a628c2fc4f 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -42,7 +42,7 @@ jobs: with: files: snyk.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 if: steps.check_files.outputs.files_exists == 'true' with: sarif_file: snyk.sarif diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index fefb11ccea..f17c4f498e 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -34,4 +34,4 @@ jobs: objects.githubusercontent.com:443 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Typos - uses: crate-ci/typos@2872c382bb9668d4baa5eade234dcbc0048ca2cf # v1.28.2 + uses: crate-ci/typos@d1c850b2b5d502763520c25fb4a6a1128ad99bd9 # v1.28.3 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 5386428435..13dd9cf96c 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -36,7 +36,7 @@ jobs: with: files: results.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 if: steps.check_files.outputs.files_exists == 'true' with: sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 8f6fc3c8dd..e3764d839f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ jitwatch.out .settings .project .gradle +.kotlin .vscode .idea *.iml diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java index 3d5d2314f2..97ff1a6a14 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java @@ -3163,6 +3163,7 @@ T expireAfterAccessOrder(boolean oldest, Function<@Nullable V, @Nullable V> * @param mappingFunction the mapping function to compute a value * @return the computed value */ + @SuppressWarnings("NullAway") T snapshot(Iterable> iterable, Function<@Nullable V, @Nullable V> transformer, Function>, T> mappingFunction) { requireNonNull(mappingFunction); @@ -3901,10 +3902,10 @@ public void run() { */ // public final void quietlyComplete() {} - @Override public void setRawResult(@Nullable Void v) {} @Override public void complete(@Nullable Void value) {} - @Override public void completeExceptionally(Throwable ex) {} + @Override public void setRawResult(@Nullable Void value) {} @Override public @Nullable Void getRawResult() { return null; } + @Override public void completeExceptionally(@Nullable Throwable t) {} @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } } @@ -4009,6 +4010,7 @@ static final class BoundedPolicy implements Policy { @Override public @Nullable V getIfPresentQuietly(K key) { return transformer.apply(cache.getIfPresentQuietly(key)); } + @SuppressWarnings("NullAway") @Override public @Nullable CacheEntry getEntryIfPresentQuietly(K key) { Node node = cache.data.get(cache.nodeFactory.newLookupKey(key)); return (node == null) ? null : cache.nodeToCacheEntry(node, transformer); diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LinkedDeque.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LinkedDeque.java index 9c3fed30df..d0c4ab1b8a 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LinkedDeque.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LinkedDeque.java @@ -55,14 +55,14 @@ interface LinkedDeque extends Deque { * * @param e the linked element */ - boolean isFirst(E e); + boolean isFirst(@Nullable E e); /** * Returns if the element is at the back of the deque. * * @param e the linked element */ - boolean isLast(E e); + boolean isLast(@Nullable E e); /** * Moves the element to the front of the deque so that it becomes the first element. diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsMapTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsMapTest.java index 9b308fe6be..9466536d18 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsMapTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsMapTest.java @@ -28,6 +28,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -1841,7 +1842,9 @@ public void keySet_removeAll_partial(Map map, CacheContext context) { assertThat(map.keySet().removeAll(context.firstMiddleLastKeys())).isTrue(); assertThat(map).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -2017,7 +2020,9 @@ public void keySet_retainAll_partial(Map map, CacheContext context) { assertThat(map.keySet().retainAll(expected.keySet())).isTrue(); assertThat(map).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -2149,6 +2154,7 @@ public void keySpliterator_estimateSize(Map map, CacheContext context) /* --------------- Values --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void values_toArray_null(Map map, CacheContext context) { @@ -2249,7 +2255,8 @@ public void values_removeAll_none_populated(Map map, CacheContext cont public void values_removeAll_partial(Map map, CacheContext context) { var expected = new HashMap<>(context.original()); expected.keySet().removeAll(context.firstMiddleLastKeys()); - var removed = Maps.asMap(context.firstMiddleLastKeys(), context.original()::get); + var removed = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); assertThat(map.values().removeAll(removed.values())).isTrue(); assertThat(map).isEqualTo(expected); @@ -2418,7 +2425,8 @@ public void values_retainAll_none_populated(Map map, CacheContext cont public void values_retainAll_partial(Map map, CacheContext context) { var expected = new HashMap<>(context.original()); expected.keySet().removeAll(context.firstMiddleLastKeys()); - var removed = Maps.asMap(context.firstMiddleLastKeys(), context.original()::get); + var removed = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); assertThat(map.values().retainAll(expected.values())).isTrue(); assertThat(map).isEqualTo(expected); @@ -2678,7 +2686,8 @@ public void entrySet_removeAll_none_populated(Map map, CacheContext co @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL) public void entrySet_removeAll_partial(Map map, CacheContext context) { - var removed = Maps.asMap(context.firstMiddleLastKeys(), context.original()::get); + var removed = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); var expected = new HashMap<>(context.original()); expected.entrySet().removeAll(removed.entrySet()); @@ -2888,7 +2897,8 @@ public void entrySet_retainAll_none_populated(Map map, CacheContext co @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL) public void entrySet_retainAll_partial(Map map, CacheContext context) { - var removed = Maps.asMap(context.firstMiddleLastKeys(), context.original()::get); + var removed = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); var expected = new HashMap<>(context.original()); expected.entrySet().removeAll(removed.entrySet()); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncAsMapTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncAsMapTest.java index 57dd96cb68..c4c0534b4b 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncAsMapTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncAsMapTest.java @@ -28,6 +28,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -292,8 +293,8 @@ public void put_insert(AsyncCache cache, CacheContext context) { public void put_replace_sameValue(AsyncCache cache, CacheContext context) { var replaced = new HashMap(); for (Int key : context.firstMiddleLastKeys()) { - var oldValue = cache.asMap().get(key); - var value = cache.asMap().get(key).thenApply(val -> intern(new Int(val))); + var oldValue = requireNonNull(cache.asMap().get(key)); + var value = oldValue.thenApply(val -> intern(new Int(val))); assertThat(cache.asMap().put(key, value)).isSameInstanceAs(oldValue); assertThat(cache).containsEntry(key, value); replaced.put(key, context.original().get(key)); @@ -307,7 +308,7 @@ public void put_replace_sameValue(AsyncCache cache, CacheContext conte @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void put_replace_sameInstance(AsyncCache cache, CacheContext context) { for (Int key : context.firstMiddleLastKeys()) { - var value = cache.asMap().get(key); + var value = requireNonNull(cache.asMap().get(key)); assertThat(cache.asMap().put(key, value)).isSameInstanceAs(value); assertThat(cache).containsEntry(key, value); } @@ -435,7 +436,7 @@ public void putIfAbsent_nullKeyAndValue(AsyncCache cache, CacheContext removalListener = { Listener.DISABLED, Listener.REJECTING }) public void putIfAbsent_present(AsyncCache cache, CacheContext context) { for (Int key : context.firstMiddleLastKeys()) { - var value = cache.asMap().get(key); + var value = requireNonNull(cache.asMap().get(key)); assertThat(cache.asMap().putIfAbsent(key, key.asFuture())).isEqualTo(value); assertThat(cache).containsEntry(key, value); } @@ -589,8 +590,8 @@ public void replace_failure(AsyncCache cache, CacheContext context) { public void replace_sameValue(AsyncCache cache, CacheContext context) { var replaced = new HashMap(); for (Int key : context.firstMiddleLastKeys()) { - var oldValue = cache.asMap().get(key); - var newValue = cache.asMap().get(key).thenApply(val -> intern(new Int(val))); + var oldValue = requireNonNull(cache.asMap().get(key)); + var newValue = oldValue.thenApply(val -> intern(new Int(val))); assertThat(cache.asMap().replace(key, newValue)).isSameInstanceAs(oldValue); assertThat(cache).containsEntry(key, newValue); replaced.put(key, context.original().get(key)); @@ -718,8 +719,8 @@ public void replaceConditionally_wrongOldValue(AsyncCache cache, Cache public void replaceConditionally_sameValue(AsyncCache cache, CacheContext context) { var replaced = new HashMap(); for (Int key : context.firstMiddleLastKeys()) { - var oldValue = cache.asMap().get(key); - var newValue = cache.asMap().get(key).thenApply(val -> intern(new Int(val))); + var oldValue = requireNonNull(cache.asMap().get(key)); + var newValue = oldValue.thenApply(val -> intern(new Int(val))); assertThat(cache.asMap().replace(key, oldValue, newValue)).isTrue(); assertThat(cache).containsEntry(key, newValue); replaced.put(key, context.original().get(key)); @@ -1310,7 +1311,7 @@ public void merge_absent(AsyncCache cache, CacheContext context) { public void merge_sameValue(AsyncCache cache, CacheContext context) { var replaced = new HashMap>(); for (Int key : context.firstMiddleLastKeys()) { - var value = cache.asMap().get(key).thenApply(Int::new); + var value = requireNonNull(cache.asMap().get(key)).thenApply(Int::new); var result = cache.asMap().merge(key, key.negate().asFuture(), (oldValue, v) -> value); assertThat(result).isSameInstanceAs(value); replaced.put(key, value); @@ -1391,12 +1392,12 @@ public void equals(AsyncCache cache, CacheContext context) { assertThat(cache.asMap().equals(map)).isTrue(); assertThat(map.equals(cache.asMap())).isTrue(); - var absent = Maps.asMap(context.absentKeys(), CompletableFuture::completedFuture); + var absent = Maps.toMap(context.absentKeys(), CompletableFuture::completedFuture); assertThat(cache.asMap().equals(absent)).isFalse(); assertThat(absent.equals(cache.asMap())).isFalse(); if (!cache.asMap().isEmpty()) { - var other = Maps.asMap(cache.asMap().keySet(), CompletableFuture::completedFuture); + var other = Maps.toMap(cache.asMap().keySet(), CompletableFuture::completedFuture); assertThat(cache.asMap().equals(other)).isFalse(); assertThat(other.equals(cache.asMap())).isFalse(); } @@ -1566,7 +1567,9 @@ public void keySet_removeAll_partial(AsyncCache cache, CacheContext co assertThat(cache.asMap().keySet().removeAll(context.firstMiddleLastKeys())).isTrue(); assertThat(cache.synchronous().asMap()).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -1739,7 +1742,9 @@ public void keySet_retainAll_partial(AsyncCache cache, CacheContext co assertThat(cache.asMap().keySet().retainAll(expected.keySet())).isTrue(); assertThat(cache.asMap()).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -1872,6 +1877,7 @@ public void keySpliterator_estimateSize(AsyncCache cache, CacheContext /* ---------------- Values -------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void values_toArray_null(AsyncCache cache, CacheContext context) { @@ -1978,12 +1984,15 @@ public void values_removeAll_none_populated(AsyncCache cache, CacheCon public void values_removeAll_partial(AsyncCache cache, CacheContext context) { var expected = new HashMap<>(context.original()); expected.keySet().removeAll(context.firstMiddleLastKeys()); - var removed = Maps.asMap(context.firstMiddleLastKeys(), cache.asMap()::get); + var removed = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(cache.asMap().get(key))); assertThat(cache.asMap().values().removeAll(removed.values())).isTrue(); assertThat(cache.synchronous().asMap()).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -2026,7 +2035,7 @@ public void values_remove_none(AsyncCache cache, CacheContext context) @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL) public void values_remove(AsyncCache cache, CacheContext context) { - var future = cache.asMap().get(context.firstKey()); + var future = requireNonNull(cache.asMap().get(context.firstKey())); assertThat(cache.asMap().values().remove(future)).isTrue(); var expected = new HashMap<>(context.original()); expected.remove(context.firstKey()); @@ -2167,7 +2176,9 @@ public void values_retainAll_partial(AsyncCache cache, CacheContext co assertThat(cache.asMap().values().retainAll(expected.values())).isTrue(); assertThat(cache.asMap()).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -2438,14 +2449,17 @@ public void entrySet_removeAll_none_populated(AsyncCache cache, CacheC @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL) public void entrySet_removeAll_partial(AsyncCache cache, CacheContext context) { - var removed = Maps.asMap(context.firstMiddleLastKeys(), cache.asMap()::get); + var removed = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(cache.asMap().get(key))); var expected = new HashMap<>(context.original()); expected.keySet().removeAll(removed.keySet()); assertThat(cache.asMap().entrySet().removeAll(removed.entrySet())).isTrue(); assertThat(cache.synchronous().asMap()).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats @@ -2656,7 +2670,9 @@ public void entrySet_retainAll_partial(AsyncCache cache, CacheContext assertThat(cache.asMap().entrySet().retainAll(expected.entrySet())).isTrue(); assertThat(cache.asMap()).isEqualTo(expected); assertThat(context).removalNotifications().withCause(EXPLICIT) - .contains(Maps.asMap(context.firstMiddleLastKeys(), context.original()::get)).exclusively(); + .contains(Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key)))) + .exclusively(); } @CheckNoStats diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncCacheTest.java index 908b1b6157..6a0cf023a6 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncCacheTest.java @@ -29,6 +29,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -85,6 +86,7 @@ public final class AsyncCacheTest { /* --------------- getIfPresent --------------- */ + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getIfPresent_nullKey(AsyncCache cache, CacheContext context) { @@ -111,12 +113,14 @@ public void getIfPresent_present(AsyncCache cache, CacheContext contex /* --------------- getFunc --------------- */ @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getFunc_nullKey(AsyncCache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.get(null, key -> null)); } @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getFunc_nullLoader(AsyncCache cache, CacheContext context) { Function mappingFunction = null; @@ -124,6 +128,7 @@ public void getFunc_nullLoader(AsyncCache cache, CacheContext context) } @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getFunc_nullKeyAndLoader(AsyncCache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.get(null, (Function) null)); @@ -133,6 +138,7 @@ public void getFunc_nullKeyAndLoader(AsyncCache cache, CacheContext co @Test(dataProvider = "caches") public void getFunc_absent_null(AsyncCache cache, CacheContext context) { Int key = context.absentKey(); + @SuppressWarnings("NullAway") var valueFuture = cache.get(key, k -> null); assertThat(context).stats().hits(0).misses(1).success(0).failures(1); @@ -146,6 +152,7 @@ public void getFunc_absent_null_async(AsyncCache cache, CacheContext c Int key = context.absentKey(); var ready = new AtomicBoolean(); var done = new AtomicBoolean(); + @SuppressWarnings("NullAway") var valueFuture = cache.get(key, k -> { await().untilTrue(ready); return null; @@ -201,6 +208,7 @@ public void getFunc_absent_failure_async(AsyncCache cache, CacheContex @CacheSpec(executor = CacheExecutor.THREADED, executorFailure = ExecutorFailure.IGNORED) public void getFunc_absent_cancelled(AsyncCache cache, CacheContext context) { var done = new AtomicBoolean(); + @SuppressWarnings("NullAway") var valueFuture = cache.get(context.absentKey(), k -> { await().until(done::get); return null; @@ -241,6 +249,7 @@ public void getFunc_present(AsyncCache cache, CacheContext context) { /* --------------- getBiFunc --------------- */ @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getBiFunc_nullKey(AsyncCache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> @@ -248,6 +257,7 @@ public void getBiFunc_nullKey(AsyncCache cache, CacheContext context) } @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getBiFunc_nullLoader(AsyncCache cache, CacheContext context) { BiFunction> mappingFunction = null; @@ -255,6 +265,7 @@ public void getBiFunc_nullLoader(AsyncCache cache, CacheContext contex } @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getBiFunc_nullKeyAndLoader(AsyncCache cache, CacheContext context) { BiFunction> mappingFunction = null; @@ -372,6 +383,7 @@ public void getBiFunc_present(AsyncCache cache, CacheContext context) /* --------------- getAllFunc --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllFunction_nullKeys(AsyncCache cache, CacheContext context) { @@ -380,6 +392,7 @@ public void getAllFunction_nullKeys(AsyncCache cache, CacheContext con } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllFunction_nullKeys_nullFunction( @@ -389,6 +402,7 @@ public void getAllFunction_nullKeys_nullFunction( } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllFunction_nullFunction(AsyncCache cache, CacheContext context) { @@ -413,11 +427,12 @@ public void getAllFunction_iterable_empty(AsyncCache cache, CacheConte assertThat(result).isExhaustivelyEmpty(); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllFunction_nullLookup(AsyncCache cache, CacheContext context) { var result = cache.getAll(context.firstMiddleLastKeys(), - keys -> Maps.asMap(keys, Int::negate)).join(); + keys -> Maps.toMap(keys, Int::negate)).join(); assertThat(result.containsValue(null)).isFalse(); assertThat(result.containsKey(null)).isFalse(); assertThat(result.get(null)).isNull(); @@ -443,6 +458,7 @@ public void getAllFunction_immutable_result(AsyncCache cache, CacheCon } @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void getAllFunction_absent_null(AsyncCache cache, CacheContext context) { assertThat(cache.getAll(context.absentKeys(), keys -> null)) @@ -609,6 +625,7 @@ public void getAllFunction_canceled_individual(AsyncCache cache, Cache }); for (var key : context.absentKeys()) { var future = cache.getIfPresent(key); + requireNonNull(future); future.cancel(true); assertThat(future).hasCompletedExceptionally(); } @@ -655,6 +672,7 @@ public void getAllFunction_badLoader(AsyncCache cache, CacheContext co /* --------------- getAllBiFunc --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllBifunction_nullKeys(AsyncCache cache, CacheContext context) { @@ -663,6 +681,7 @@ public void getAllBifunction_nullKeys(AsyncCache cache, CacheContext c } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllBifunction_nullKeys_nullBifunction( @@ -672,6 +691,7 @@ public void getAllBifunction_nullKeys_nullBifunction( } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllBifunction_nullBifunction(AsyncCache cache, CacheContext context) { @@ -702,7 +722,7 @@ public void getAllBifunction_iterable_empty(AsyncCache cache, CacheCon @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllBiFunction_nullLookup(AsyncCache cache, CacheContext context) { var result = cache.getAll(context.firstMiddleLastKeys(), (keys, executor) -> - CompletableFuture.completedFuture(Maps.asMap(keys, Int::negate))).join(); + CompletableFuture.completedFuture(Maps.toMap(keys, Int::negate))).join(); assertThat(result.containsValue(null)).isFalse(); assertThat(result.containsKey(null)).isFalse(); assertThat(result.get(null)).isNull(); @@ -938,6 +958,7 @@ public void getAllBifunction_canceled_individual( }); for (var key : context.absentKeys()) { var future = cache.getIfPresent(key); + requireNonNull(future); future.cancel(true); assertThat(future).hasCompletedExceptionally(); } @@ -1010,6 +1031,7 @@ public void getAllBifunction_early_success(AsyncCache cache, CacheCont var result = cache.getAll(context.absentKeys(), (keysToLoad, executor) -> bulk); var future = cache.asMap().get(key); + requireNonNull(future); future.complete(value); bulk.complete(context.absent()); // obtrudes the future's value @@ -1027,6 +1049,8 @@ public void getAllBifunction_early_failure(AsyncCache cache, CacheCont var bulk = new CompletableFuture>(); var result = cache.getAll(context.absentKeys(), (keysToLoad, executor) -> bulk); var future = cache.asMap().get(key); + + requireNonNull(future); future.completeExceptionally(error); bulk.complete(context.absent()); @@ -1043,6 +1067,7 @@ public void getAllBifunction_early_failure(AsyncCache cache, CacheCont /* --------------- put --------------- */ + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void put_nullKey(AsyncCache cache, CacheContext context) { @@ -1050,12 +1075,14 @@ public void put_nullKey(AsyncCache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.put(null, value)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void put_nullValue(AsyncCache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.put(context.absentKey(), null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void put_nullKeyAndValue(AsyncCache cache, CacheContext context) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java index 70a6fdc282..a0f20e2b84 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java @@ -27,6 +27,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.slf4j.event.Level.WARN; @@ -84,6 +85,7 @@ public final class AsyncLoadingCacheTest { /* --------------- get --------------- */ @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void get_null(AsyncLoadingCache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.get(null)); @@ -163,6 +165,7 @@ public void get_present(AsyncLoadingCache cache, CacheContext context) /* --------------- getAll --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAll_iterable_null(AsyncLoadingCache cache, CacheContext context) { @@ -428,7 +431,7 @@ public void getAll_canceled_individual(CacheContext context) { var cache = context.buildAsync(loader); var bulk = cache.getAll(context.absentKeys()); for (var key : context.absentKeys()) { - var future = cache.getIfPresent(key); + var future = requireNonNull(cache.getIfPresent(key)); future.cancel(true); assertThat(future).hasCompletedExceptionally(); } @@ -537,6 +540,7 @@ public void refresh(CacheContext context) { await().untilAsserted(() -> assertThat(cache).containsEntry(key, key.negate())); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, compute = Compute.ASYNC) public void refresh_nullFuture_load(CacheContext context) { @@ -552,6 +556,7 @@ public void refresh_nullFuture_reload(CacheContext context) { @Override public CompletableFuture asyncLoad(Int key, Executor executor) { throw new IllegalStateException(); } + @SuppressWarnings("NullAway") @Override public CompletableFuture asyncReload( Int key, Int oldValue, Executor executor) { return null; @@ -687,6 +692,7 @@ public void asyncReload() throws Exception { } @Test + @SuppressWarnings("NullAway") public void bulk_function_null() { Function, Map> f = null; assertThrows(NullPointerException.class, () -> AsyncCacheLoader.bulk(f)); @@ -710,6 +716,7 @@ public void bulk_function_present() throws Exception { } @Test + @SuppressWarnings("NullAway") public void bulk_bifunction_null() { BiFunction, Executor, CompletableFuture>> f = null; assertThrows(NullPointerException.class, () -> AsyncCacheLoader.bulk(f)); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedBufferTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedBufferTest.java index ebc45b5bf0..ecfd7838e8 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedBufferTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedBufferTest.java @@ -83,6 +83,7 @@ public void offerAndDrain(BoundedBuffer buffer) { @Test public void overflow() { + @SuppressWarnings("NullAway") var buffer = new BoundedBuffer.RingBuffer(null); buffer.writeCounter = Long.MAX_VALUE; buffer.readCounter = Long.MAX_VALUE; diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java index 42d54382ab..d196cedc32 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java @@ -46,6 +46,7 @@ import static java.lang.Thread.State.BLOCKED; import static java.lang.Thread.State.WAITING; import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -79,6 +80,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Future; @@ -90,8 +92,11 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.IntStream; +import java.util.stream.Stream; import org.apache.commons.lang3.mutable.MutableInt; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.testng.annotations.Listeners; @@ -140,6 +145,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Range; +import com.google.common.collect.Streams; import com.google.common.testing.GcFinalization; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Uninterruptibles; @@ -336,13 +342,11 @@ public void scheduleDrainBuffers() { @Test public void rescheduleDrainBuffers() { - var evicting = new AtomicBoolean(); var done = new AtomicBoolean(); - var evictionListener = new RemovalListener() { - @Override public void onRemoval(Int key, Int value, RemovalCause cause) { - evicting.set(true); - await().untilTrue(done); - } + var evicting = new AtomicBoolean(); + RemovalListener evictionListener = (key, value, cause) -> { + evicting.set(true); + await().untilTrue(done); }; var cache = asBoundedLocalCache(Caffeine.newBuilder() .executor(CacheExecutor.THREADED.create()) @@ -428,7 +432,8 @@ public void rescheduleCleanUpIfIncomplete_notScheduled_future( BoundedLocalCache cache, CacheContext context) { reset(context.scheduler()); cache.drainStatus = REQUIRED; - cache.pacer().future = new CompletableFuture<>(); + var pacer = requireNonNull(cache.pacer()); + pacer.future = new CompletableFuture<>(); cache.rescheduleCleanUpIfIncomplete(); verifyNoInteractions(context.scheduler()); @@ -441,7 +446,8 @@ public void rescheduleCleanUpIfIncomplete_notScheduled_locked( BoundedLocalCache cache, CacheContext context) { reset(context.scheduler()); cache.drainStatus = REQUIRED; - cache.pacer().cancel(); + var pacer = requireNonNull(cache.pacer()); + pacer.cancel(); var done = new AtomicBoolean(); cache.evictionLock.lock(); @@ -467,7 +473,8 @@ public void rescheduleCleanUpIfIncomplete_scheduled_noFuture( when(context.scheduler().schedule(any(), any(), anyLong(), any())) .thenReturn(new CompletableFuture<>()); cache.drainStatus = REQUIRED; - cache.pacer().cancel(); + var pacer = requireNonNull(cache.pacer()); + pacer.cancel(); cache.rescheduleCleanUpIfIncomplete(); assertThat(cache.pacer().isScheduled()).isTrue(); @@ -482,7 +489,8 @@ public void rescheduleCleanUpIfIncomplete_scheduled_doneFuture( when(context.scheduler().schedule(any(), any(), anyLong(), any())) .thenReturn(new CompletableFuture<>()); - cache.pacer().future = DisabledFuture.INSTANCE; + var pacer = requireNonNull(cache.pacer()); + pacer.future = DisabledFuture.instance(); cache.drainStatus = REQUIRED; cache.rescheduleCleanUpIfIncomplete(); @@ -546,7 +554,7 @@ public void afterWrite_exception() { var expected = new RuntimeException(); var cache = new BoundedLocalCache( Caffeine.newBuilder(), /* cacheLoader= */ null, /* isAsync= */ false) { - @Override void maintenance(Runnable task) { + @Override void maintenance(@Nullable Runnable task) { throw expected; } }; @@ -662,7 +670,7 @@ public void overflow_update_one(BoundedLocalCache cache, CacheContext @CacheSpec(population = Population.FULL, maximumSize = Maximum.UNREACHABLE, weigher = CacheWeigher.VALUE) public void overflow_update_many(BoundedLocalCache cache, CacheContext context) { - var updated = Maps.asMap(context.firstMiddleLastKeys(), key -> Int.MAX_VALUE); + var updated = Maps.toMap(context.firstMiddleLastKeys(), key -> Int.MAX_VALUE); cache.setWeightedSize(BoundedLocalCache.MAXIMUM_CAPACITY); cache.evictionLock.lock(); try { @@ -689,8 +697,8 @@ public void overflow_update_many(BoundedLocalCache cache, CacheContext @Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, population = Population.EMPTY, maximumSize = Maximum.ONE) public void evict_alreadyRemoved(BoundedLocalCache cache, CacheContext context) { - var oldEntry = Iterables.get(context.absent().entrySet(), 0); - var newEntry = Iterables.get(context.absent().entrySet(), 1); + var oldEntry = requireNonNull(Iterables.get(context.absent().entrySet(), 0)); + var newEntry = requireNonNull(Iterables.get(context.absent().entrySet(), 1)); var oldValue = cache.put(oldEntry.getKey(), oldEntry.getValue()); assertThat(oldValue).isNull(); @@ -698,7 +706,7 @@ public void evict_alreadyRemoved(BoundedLocalCache cache, CacheContext cache.evictionLock.lock(); try { var lookupKey = cache.nodeFactory.newLookupKey(oldEntry.getKey()); - var node = cache.data.get(lookupKey); + var node = requireNonNull(cache.data.get(lookupKey)); checkStatus(node, Status.ALIVE); ConcurrentTestHarness.execute(() -> { var value = cache.put(newEntry.getKey(), newEntry.getValue()); @@ -822,10 +830,11 @@ public void evict_victim_lru(BoundedLocalCache cache, CacheContext con var candidate = cache.evictFromWindow(); assertThat(candidate).isNotNull(); - var expected = FluentIterable - .from(cache.accessOrderProbationDeque()) - .append(cache.accessOrderProtectedDeque()) - .transform(Node::getKey).toList(); + var expected = Stream + .concat(cache.accessOrderProbationDeque().stream(), + cache.accessOrderProtectedDeque().stream()) + .map(Node::getKey) + .collect(toImmutableList()); cache.setMaximumSize(0L); cache.cleanUp(); @@ -903,11 +912,12 @@ public void evict_candidateIsVictim(BoundedLocalCache cache, CacheCont Arrays.fill(cache.frequencySketch().table, 0L); cache.setMainProtectedWeightedSize(context.maximumSize() - cache.windowWeightedSize()); - var expected = FluentIterable - .from(cache.accessOrderWindowDeque()) - .append(cache.accessOrderProbationDeque()) - .append(cache.accessOrderProtectedDeque()) - .transform(Node::getKey).toList(); + var expected = Streams + .concat(cache.accessOrderWindowDeque().stream(), + cache.accessOrderProbationDeque().stream(), + cache.accessOrderProtectedDeque().stream()) + .map(Node::getKey) + .collect(toImmutableList()); cache.setMainProtectedMaximum(0L); cache.setWindowMaximum(0L); cache.setMaximum(0L); @@ -929,11 +939,12 @@ public void evict_toZero(BoundedLocalCache cache, CacheContext context } Arrays.fill(cache.frequencySketch().table, 0L); - var expected = FluentIterable - .from(cache.accessOrderWindowDeque()) - .append(cache.accessOrderProbationDeque()) - .append(cache.accessOrderProtectedDeque()) - .transform(Node::getKey).toList(); + var expected = Streams + .concat(cache.accessOrderWindowDeque().stream(), + cache.accessOrderProbationDeque().stream(), + cache.accessOrderProtectedDeque().stream()) + .map(Node::getKey) + .collect(toImmutableList()); cache.setMaximumSize(0); cache.evictEntries(); @@ -948,8 +959,8 @@ public void evict_toZero(BoundedLocalCache cache, CacheContext context public void evict_retired_candidate(BoundedLocalCache cache, CacheContext context) { cache.evictionLock.lock(); try { - var expected = cache.accessOrderWindowDeque().peekFirst(); - var key = expected.getKey(); + var expected = cache.accessOrderWindowDeque().getFirst(); + var key = requireNonNull(expected.getKey()); ConcurrentTestHarness.execute(() -> { var value = cache.remove(key); @@ -975,8 +986,8 @@ public void evict_retired_candidate(BoundedLocalCache cache, CacheCont public void evict_retired_victim(BoundedLocalCache cache, CacheContext context) { cache.evictionLock.lock(); try { - var expected = cache.accessOrderProbationDeque().peekFirst(); - var key = expected.getKey(); + var expected = cache.accessOrderProbationDeque().getFirst(); + var key = requireNonNull(expected.getKey()); ConcurrentTestHarness.execute(() -> { var value = cache.remove(key); @@ -998,13 +1009,18 @@ public void evict_retired_victim(BoundedLocalCache cache, CacheContext @Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, population = Population.EMPTY, - maximumSize = Maximum.FULL, weigher = CacheWeigher.VALUE) + maximumSize = Maximum.FULL, weigher = CacheWeigher.MOCKITO) public void evict_zeroWeight_candidate(BoundedLocalCache cache, CacheContext context) { + when(context.weigher().weigh(any(), any())).thenAnswer(invocation -> { + Int value = invocation.getArgument(1); + return Math.abs(value.intValue()); + }); + for (int i = 0; i < context.maximumSize(); i++) { assertThat(cache.put(Int.valueOf(i), Int.valueOf(1))).isNull(); } - var candidate = cache.accessOrderWindowDeque().peekFirst(); + var candidate = cache.accessOrderWindowDeque().getFirst(); cache.setWindowWeightedSize(cache.windowWeightedSize() - candidate.getWeight()); cache.setWeightedSize(cache.weightedSize() - candidate.getWeight()); candidate.setPolicyWeight(0); @@ -1019,13 +1035,18 @@ public void evict_zeroWeight_candidate(BoundedLocalCache cache, CacheC @Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, population = Population.EMPTY, - maximumSize = Maximum.FULL, weigher = CacheWeigher.VALUE) + maximumSize = Maximum.FULL, weigher = CacheWeigher.MOCKITO) public void evict_zeroWeight_victim(BoundedLocalCache cache, CacheContext context) { + when(context.weigher().weigh(any(), any())).thenAnswer(invocation -> { + Int value = invocation.getArgument(1); + return Math.abs(value.intValue()); + }); + for (int i = 0; i < context.maximumSize(); i++) { assertThat(cache.put(Int.valueOf(i), Int.valueOf(1))).isNull(); } - var victim = cache.accessOrderProbationDeque().peekFirst(); + var victim = cache.accessOrderProbationDeque().getFirst(); cache.setWeightedSize(cache.weightedSize() - victim.getWeight()); victim.setPolicyWeight(0); victim.setWeight(0); @@ -1134,7 +1155,9 @@ public void evict_update_entryTooBig_window( cache.putAll(Map.of(Int.valueOf(9), Int.valueOf(9), Int.valueOf(1), Int.valueOf(1))); var lookupKey = cache.nodeFactory.newLookupKey(Int.valueOf(1)); - assertThat(cache.data.get(lookupKey).inWindow()).isTrue(); + var node = requireNonNull(cache.data.get(lookupKey)); + assertThat(node.inWindow()).isTrue(); + var oldValue = cache.put(Int.valueOf(1), Int.valueOf(20)); assertThat(oldValue).isEqualTo(1); @@ -1160,7 +1183,9 @@ public void evict_update_entryTooBig_probation( } var lookupKey = cache.nodeFactory.newLookupKey(Int.valueOf(1)); - assertThat(cache.data.get(lookupKey).inMainProbation()).isTrue(); + var node = requireNonNull(cache.data.get(lookupKey)); + assertThat(node.inMainProbation()).isTrue(); + var oldValue = cache.put(Int.valueOf(1), Int.valueOf(20)); assertThat(oldValue).isEqualTo(1); @@ -1190,7 +1215,9 @@ public void evict_update_entryTooBig_protected( cache.cleanUp(); var lookupKey = cache.nodeFactory.newLookupKey(Int.valueOf(1)); - assertThat(cache.data.get(lookupKey).inMainProtected()).isTrue(); + var node = requireNonNull(cache.data.get(lookupKey)); + assertThat(node.inMainProtected()).isTrue(); + var oldValue = cache.put(Int.valueOf(1), Int.valueOf(20)); assertThat(oldValue).isEqualTo(1); @@ -1216,6 +1243,8 @@ public void evict_resurrect_collected(BoundedLocalCache cache, CacheCo assertThat(initialValue).isNull(); var node = cache.data.get(cache.referenceKey(key)); + assertThat(node).isNotNull(); + @SuppressWarnings("unchecked") var ref = (Reference) node.getValueReference(); ref.enqueue(); @@ -1233,7 +1262,10 @@ public void evict_resurrect_collected(BoundedLocalCache cache, CacheCo }); await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); return newValue; }); @@ -1268,7 +1300,10 @@ public void evict_resurrect_weight(Cache> cache, CacheContext con await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); return List.of(); }); @@ -1302,7 +1337,10 @@ public void evict_resurrect_expireAfter(Cache cache, CacheContext cont await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); return key.negate(); }); await().untilTrue(done); @@ -1333,7 +1371,10 @@ public void evict_resurrect_expireAfterAccess(Cache cache, CacheContex await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); cache.policy().expireAfterAccess().orElseThrow().setExpiresAfter(Duration.ofHours(1)); return v; }); @@ -1364,7 +1405,10 @@ public void evict_resurrect_expireAfterWrite(Cache cache, CacheContext await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); cache.policy().expireAfterWrite().orElseThrow().setExpiresAfter(Duration.ofHours(1)); return v; }); @@ -1395,7 +1439,10 @@ public void evict_resurrect_expireAfterWrite_entry(Cache cache, CacheC await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); return key.negate(); }); await().untilTrue(done); @@ -1417,7 +1464,7 @@ public void evict_resurrect_expireAfterVar( var started = new AtomicBoolean(); var done = new AtomicBoolean(); var evictor = new AtomicReference(); - var node = cache.data.get(cache.referenceKey(key)); + var node = requireNonNull(cache.data.get(cache.referenceKey(key))); synchronized (node) { context.ticker().advance(Duration.ofHours(1)); ConcurrentTestHarness.execute(() -> { @@ -1429,7 +1476,10 @@ public void evict_resurrect_expireAfterVar( await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(evictor.get().getState())); + await().until(() -> { + var thread = evictor.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); node.setVariableTime(context.ticker().read() + TimeUnit.DAYS.toNanos(1)); } await().untilTrue(done); @@ -1500,7 +1550,8 @@ public void evictEntry_absent(BoundedLocalCache cache, CacheContext co public void updateRecency_onGet(BoundedLocalCache cache, CacheContext context) { var first = firstBeforeAccess(cache, context); updateRecency(cache, context, () -> { - var value = cache.get(first.getKey()); + var key = requireNonNull(first.getKey()); + var value = cache.get(key); assertThat(value).isNotNull(); }); } @@ -1510,7 +1561,8 @@ public void updateRecency_onGet(BoundedLocalCache cache, CacheContext public void updateRecency_onPutIfAbsent(BoundedLocalCache cache, CacheContext context) { var first = firstBeforeAccess(cache, context); updateRecency(cache, context, () -> { - var oldValue = cache.putIfAbsent(first.getKey(), first.getKey()); + var key = requireNonNull(first.getKey()); + var oldValue = cache.putIfAbsent(key, key); assertThat(oldValue).isNotNull(); }); } @@ -1520,7 +1572,8 @@ public void updateRecency_onPutIfAbsent(BoundedLocalCache cache, Cache public void updateRecency_onPut(BoundedLocalCache cache, CacheContext context) { var first = firstBeforeAccess(cache, context); updateRecency(cache, context, () -> { - var oldValue = cache.put(first.getKey(), first.getKey()); + var key = requireNonNull(first.getKey()); + var oldValue = cache.put(key, key); assertThat(oldValue).isNotNull(); }); } @@ -1530,7 +1583,8 @@ public void updateRecency_onPut(BoundedLocalCache cache, CacheContext public void updateRecency_onReplace(BoundedLocalCache cache, CacheContext context) { var first = firstBeforeAccess(cache, context); updateRecency(cache, context, () -> { - var oldValue = cache.replace(first.getKey(), first.getKey()); + var key = requireNonNull(first.getKey()); + var oldValue = cache.replace(key, key); assertThat(oldValue).isNotNull(); }); } @@ -1540,10 +1594,11 @@ public void updateRecency_onReplace(BoundedLocalCache cache, CacheCont public void updateRecency_onReplaceConditionally( BoundedLocalCache cache, CacheContext context) { var first = firstBeforeAccess(cache, context); - Int value = first.getValue(); + Int value = requireNonNull(first.getValue()); updateRecency(cache, context, () -> { - boolean replaced = cache.replace(first.getKey(), value, value); + var key = requireNonNull(first.getKey()); + boolean replaced = cache.replace(key, value, value); assertThat(replaced).isTrue(); }); } @@ -1551,8 +1606,8 @@ public void updateRecency_onReplaceConditionally( private static Node firstBeforeAccess( BoundedLocalCache cache, CacheContext context) { return context.isZeroWeighted() - ? cache.accessOrderWindowDeque().peek() - : cache.accessOrderProbationDeque().peek(); + ? cache.accessOrderWindowDeque().getFirst() + : cache.accessOrderProbationDeque().getFirst(); } private static void updateRecency(BoundedLocalCache cache, @@ -1575,6 +1630,7 @@ private static void updateRecency(BoundedLocalCache cache, @CacheSpec(compute = Compute.SYNC, population = Population.EMPTY, maximumSize = Maximum.FULL) public void exceedsMaximumBufferSize_onRead( BoundedLocalCache cache, CacheContext context) { + @SuppressWarnings("NullAway") var dummy = cache.nodeFactory.newNode( new WeakKeyReference<>(null, null), null, null, 1, 0); cache.frequencySketch().ensureCapacity(1); @@ -1916,20 +1972,23 @@ public void put_warnIfEvictionBlocked(BoundedLocalCache cache, CacheCo var halfWaitTime = Duration.ofNanos(WARN_AFTER_LOCK_WAIT_NANOS / 2); await().until(cache.evictionLock::hasQueuedThreads); + assertThat(thread.get()).isNotNull(); thread.get().interrupt(); Uninterruptibles.sleepUninterruptibly(halfWaitTime); assertThat(cache.evictionLock.hasQueuedThreads()).isTrue(); - assertThat(testLogger.get().getAllLoggingEvents()).isEmpty(); + var threadLogger = requireNonNull(testLogger.get()); + assertThat(threadLogger.getAllLoggingEvents()).isEmpty(); Uninterruptibles.sleepUninterruptibly(halfWaitTime.plusMillis(500)); - await().until(() -> !testLogger.get().getAllLoggingEvents().isEmpty()); + await().until(() -> !threadLogger.getAllLoggingEvents().isEmpty()); assertThat(cache.evictionLock.hasQueuedThreads()).isTrue(); var event = Iterables.getOnlyElement(TestLoggerFactory.getAllLoggingEvents().stream() .filter(e -> e.getLevel() == WARN) .collect(toImmutableList())); + requireNonNull(event); assertThat(event.getFormattedMessage()).contains("excessive wait times"); assertThat(event.getThrowable().orElseThrow()).isInstanceOf(TimeoutException.class); } finally { @@ -1945,7 +2004,7 @@ public void put_spinToCompute(BoundedLocalCache cache, CacheContext co var initialValue = cache.put(context.absentKey(), context.absentValue()); assertThat(initialValue).isNull(); - var node = cache.data.get(context.absentKey()); + var node = requireNonNull(cache.data.get(context.absentKey())); node.retire(); var future = new Future[1]; @@ -1959,7 +2018,10 @@ public void put_spinToCompute(BoundedLocalCache cache, CacheContext co var threadState = EnumSet.of(BLOCKED, WAITING); await().untilAtomic(writer, is(not(nullValue()))); - await().until(() -> threadState.contains(writer.get().getState())); + await().until(() -> { + var thread = writer.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); return null; }); @@ -1979,19 +2041,23 @@ public void putIfAbsent_expireAfterRead(BoundedLocalCache cache, Cache context.ticker().advance(Duration.ofHours(1)); var result = new AtomicReference(); long currentDuration = 1; + requireNonNull(node); synchronized (node) { var started = new AtomicBoolean(); - var thread = new AtomicReference(); + var writer = new AtomicReference(); ConcurrentTestHarness.execute(() -> { - thread.set(Thread.currentThread()); + writer.set(Thread.currentThread()); started.set(true); var value = cache.putIfAbsent(context.firstKey(), context.absentValue()); result.set(value); }); await().untilTrue(started); var threadState = EnumSet.of(BLOCKED, WAITING); - await().until(() -> threadState.contains(thread.get().getState())); + await().until(() -> { + var thread = writer.get(); + return (thread != null) && threadState.contains(thread.getState()); + }); node.setVariableTime(context.ticker().read() + currentDuration); } @@ -2019,15 +2085,16 @@ public void unschedule_cleanUp(BoundedLocalCache cache, CacheContext c var value = cache.put(Int.valueOf(i), Int.valueOf(-i)); assertThat(value).isNull(); } - assertThat(cache.pacer().nextFireTime).isNotEqualTo(0); - assertThat(cache.pacer().future).isNotNull(); + var pacer = requireNonNull(cache.pacer()); + assertThat(pacer.nextFireTime).isNotEqualTo(0); + assertThat(pacer.future).isNotNull(); context.ticker().advance(Duration.ofHours(1)); cache.cleanUp(); verify(future).cancel(false); - assertThat(cache.pacer().nextFireTime).isEqualTo(0); - assertThat(cache.pacer().future).isNull(); + assertThat(pacer.nextFireTime).isEqualTo(0); + assertThat(pacer.future).isNull(); } @Test(dataProvider = "caches") @@ -2045,13 +2112,14 @@ public void unschedule_invalidateAll(BoundedLocalCache cache, CacheCon var value = cache.put(Int.valueOf(i), Int.valueOf(-i)); assertThat(value).isNull(); } - assertThat(cache.pacer().nextFireTime).isNotEqualTo(0); - assertThat(cache.pacer().future).isNotNull(); + var pacer = requireNonNull(cache.pacer()); + assertThat(pacer.nextFireTime).isNotEqualTo(0); + assertThat(pacer.future).isNotNull(); cache.clear(); verify(future).cancel(false); - assertThat(cache.pacer().nextFireTime).isEqualTo(0); - assertThat(cache.pacer().future).isNull(); + assertThat(pacer.nextFireTime).isEqualTo(0); + assertThat(pacer.future).isNull(); } @Test(dataProvider = "caches") @@ -2076,8 +2144,9 @@ public void expirationDelay_window(BoundedLocalCache cache, CacheConte node.setAccessTime(context.ticker().read()); } for (var node : FluentIterable.from(cache.accessOrderProbationDeque()).skip(5).toList()) { - var value = cache.get(node.getKey()); - assertThat(value).isEqualTo(node.getKey()); + var key = requireNonNull(node.getKey()); + var value = cache.get(key); + assertThat(value).isEqualTo(key); } context.ticker().advance(Duration.ofNanos(stepSize)); cache.cleanUp(); @@ -2108,8 +2177,9 @@ public void expirationDelay_probation(BoundedLocalCache cache, CacheCo node.setAccessTime(context.ticker().read()); } for (var node : FluentIterable.from(cache.accessOrderProbationDeque()).skip(5).toList()) { - var value = cache.get(node.getKey()); - assertThat(value).isEqualTo(node.getKey()); + var key = requireNonNull(node.getKey()); + var value = cache.get(key); + assertThat(value).isEqualTo(key); } context.ticker().advance(Duration.ofNanos(stepSize)); cache.cleanUp(); @@ -2133,8 +2203,9 @@ public void expirationDelay_protected(BoundedLocalCache cache, CacheCo } for (var node : FluentIterable.from(cache.accessOrderProbationDeque()).skip(5).toList()) { - var value = cache.get(node.getKey()); - assertThat(value).isEqualTo(node.getKey()); + var key = requireNonNull(node.getKey()); + var value = cache.get(key); + assertThat(value).isEqualTo(key); } context.ticker().advance(Duration.ofNanos(stepSize)); cache.cleanUp(); @@ -2227,15 +2298,26 @@ public void refreshIfNeeded_liveliness(CacheContext context) { assertThat(oldValue).isNull(); // Remove the entry after the read, but before the refresh, and leave it as retired - var node = cache.data.get(context.absentKey()); + var node = requireNonNull(cache.data.get(cache.nodeFactory.newLookupKey(context.absentKey()))); + var errors = new ConcurrentLinkedDeque(); doAnswer(invocation -> { ConcurrentTestHarness.execute(() -> { - var value = cache.remove(context.absentKey()); - assertThat(value).isEqualTo(context.absentValue()); + try { + var value = cache.remove(context.absentKey()); + assertThat(value).isEqualTo(context.absentValue()); + } catch (Throwable t) { + errors.add(t); + } }); - await().until(() -> !cache.containsKey(context.absentKey())); - assertThat(node.isRetired()).isTrue(); - return null; + + try { + await().until(() -> !cache.containsKey(context.absentKey())); + assertThat(node.isRetired()).isTrue(); + return null; + } catch (Throwable t) { + errors.add(t); + throw t; + } }).when(stats).recordHits(1); // Ensure that the refresh operation was skipped @@ -2245,6 +2327,7 @@ public void refreshIfNeeded_liveliness(CacheContext context) { var value = cache.getIfPresent(context.absentKey(), /* recordStats= */ true); assertThat(value).isEqualTo(context.absentValue()); assertThat(refreshEntry.get()).isNull(); + assertThat(errors).isEmpty(); } finally { cache.evictionLock.unlock(); } @@ -2276,7 +2359,7 @@ public CompletableFuture asyncReload(Int key, Int oldValue, Executor execut var localCache = asBoundedLocalCache(cache); cache.put(context.absentKey(), context.absentValue()); var lookupKey = localCache.nodeFactory.newLookupKey(context.absentKey()); - var node = localCache.data.get(lookupKey); + var node = requireNonNull(localCache.data.get(lookupKey)); var refreshes = localCache.refreshes(); context.ticker().advance(Duration.ofMinutes(2)); @@ -2379,7 +2462,7 @@ public void brokenEquality_eviction(BoundedLocalCache cache, assertThat(context).notifications().isEmpty(); assertThat(cache.estimatedSize()).isEqualTo(1); - var event = Iterables.getOnlyElement(TestLoggerFactory.getLoggingEvents()); + var event = requireNonNull(Iterables.getOnlyElement(TestLoggerFactory.getLoggingEvents())); assertThat(event.getThrowable().orElseThrow()).isInstanceOf(IllegalStateException.class); checkBrokenEqualityMessage(cache, key, event.getFormattedMessage()); assertThat(event.getLevel()).isEqualTo(ERROR); @@ -2408,7 +2491,7 @@ public void brokenEquality_expiration( assertThat(context).notifications().isEmpty(); assertThat(cache.estimatedSize()).isEqualTo(1); - var event = Iterables.getOnlyElement(TestLoggerFactory.getLoggingEvents()); + var event = requireNonNull(Iterables.getOnlyElement(TestLoggerFactory.getLoggingEvents())); assertThat(event.getThrowable().orElseThrow()).isInstanceOf(IllegalStateException.class); checkBrokenEqualityMessage(cache, key, event.getFormattedMessage()); assertThat(event.getLevel()).isEqualTo(ERROR); @@ -2430,7 +2513,7 @@ public void brokenEquality_clear(BoundedLocalCache cache, CacheCont assertThat(context).notifications().isEmpty(); assertThat(cache.estimatedSize()).isEqualTo(1); - var event = Iterables.getOnlyElement(TestLoggerFactory.getLoggingEvents()); + var event = requireNonNull(Iterables.getOnlyElement(TestLoggerFactory.getLoggingEvents())); assertThat(event.getThrowable().orElseThrow()).isInstanceOf(IllegalStateException.class); checkBrokenEqualityMessage(cache, key, event.getFormattedMessage()); assertThat(event.getLevel()).isEqualTo(ERROR); @@ -2515,12 +2598,11 @@ private static void testForBrokenEquality(BoundedLocalCache cac var e = assertThrows(IllegalStateException.class, () -> task.accept(key)); checkBrokenEqualityMessage(cache, key, e.getMessage()); - cache.data.clear(); } private static void checkBrokenEqualityMessage( - BoundedLocalCache cache, Object key, String msg) { + BoundedLocalCache cache, Object key, @Nullable String msg) { assertThat(msg).contains("An invalid state was detected"); assertThat(msg).contains("cache type: " + cache.getClass().getSimpleName()); assertThat(msg).contains("node type: " + cache.nodeFactory.getClass().getSimpleName()); @@ -2539,9 +2621,9 @@ public void reflectivelyConstruct() throws ReflectiveOperationException { } @Test + @SuppressWarnings("NullAway") public void cacheFactory_null() { - assertThrows(NullPointerException.class, - () -> LocalCacheFactory.loadFactory(null)); + assertThrows(NullPointerException.class, () -> LocalCacheFactory.loadFactory(null)); } @Test @@ -2638,6 +2720,7 @@ public void cacheFactory_byMethodHandle( } @Test + @SuppressWarnings("NullAway") public void nodeFactory_null() { assertThrows(NullPointerException.class, () -> NodeFactory.loadFactory(/* className= */ null)); } @@ -2712,6 +2795,7 @@ public void cache_unsupported() { @Test public void cleanupTask_ignore() { + @SuppressWarnings("NullAway") var task = new PerformCleanupTask(null); assertThat(task.getRawResult()).isNull(); assertThat(task.cancel(false)).isFalse(); @@ -2784,7 +2868,7 @@ public void policy_unsupported() { @Test(dataProvider = "caches") @CacheSpec(population = Population.SINGLETON, expiryTime = Expire.ONE_MINUTE) public void expireAfterRead_disabled(BoundedLocalCache cache, CacheContext context) { - var node = Iterables.getOnlyElement(cache.data.values()); + var node = requireNonNull(Iterables.getOnlyElement(cache.data.values())); long duration = cache.expireAfterRead(node, node.getKey(), node.getValue(), cache.expiry(), context.ticker().read()); var expiresAt = cache.expiresVariable() @@ -2837,17 +2921,18 @@ static final class BadBoundedLocalCache extends BoundedLocalCache { @SuppressWarnings("unchecked") BadBoundedLocalCache(Caffeine builder, - AsyncCacheLoader cacheLoader, boolean async) { + @Nullable AsyncCacheLoader cacheLoader, boolean async) { super(builder, (AsyncCacheLoader) cacheLoader, async); throw new IllegalStateException(); } } static final class BadLocalCacheFactory implements LocalCacheFactory { @Override public BoundedLocalCache newInstance(Caffeine builder, - AsyncCacheLoader cacheLoader, boolean async) { + @Nullable AsyncCacheLoader cacheLoader, boolean async) { throw new IllegalStateException(); } } + @NullUnmarked static final class BadNode extends Node { public BadNode() { throw new IllegalStateException(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CacheTest.java index feb55527b9..af7ea6bd3c 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CacheTest.java @@ -25,6 +25,7 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -121,6 +122,7 @@ public void estimatedSize(Cache cache, CacheContext context) { /* --------------- getIfPresent --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getIfPresent_nullKey(Cache cache, CacheContext context) { @@ -148,6 +150,7 @@ public void getIfPresent_present(Cache cache, CacheContext context) { @CacheSpec @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void get_nullKey(Cache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.get(null, identity())); @@ -155,6 +158,7 @@ public void get_nullKey(Cache cache, CacheContext context) { @CacheSpec @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void get_nullLoader(Cache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.get(context.absentKey(), null)); @@ -162,12 +166,14 @@ public void get_nullLoader(Cache cache, CacheContext context) { @CacheSpec @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void get_nullKeyAndLoader(Cache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.get(null, null)); } @CacheSpec + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void get_absent_null(Cache cache, CacheContext context) { assertThat(cache.get(context.absentKey(), k -> null)).isNull(); @@ -228,6 +234,7 @@ public void get_present(Cache cache, CacheContext context) { /* --------------- getAllPresent --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAllPresent_iterable_null(Cache cache, CacheContext context) { @@ -345,7 +352,7 @@ final class Key { keys.add(new Key()); } - Key key = Iterables.getLast(keys); + Key key = requireNonNull(Iterables.getLast(keys)); Int value = context.absentValue(); cache.put(key, value); @@ -355,6 +362,7 @@ final class Key { /* --------------- getAll --------------- */ + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAll_iterable_null(Cache cache, CacheContext context) { @@ -378,12 +386,14 @@ public void getAll_iterable_empty(Cache cache, CacheContext context) { assertThat(context).stats().hits(0).misses(0); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAll_function_null(Cache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.getAll(context.absentKeys(), null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAll_function_nullValue(Cache cache, CacheContext context) { @@ -589,13 +599,14 @@ final class Key { return 0; // to put keys in one bucket } } + @SuppressWarnings("NullAway") Cache cache = context.build(key -> null); var keys = new ArrayList(); for (int i = 0; i < Population.FULL.size(); i++) { keys.add(intern(new Key())); } - Key key = Iterables.getLast(keys); + Key key = requireNonNull(Iterables.getLast(keys)); Int value = context.absentValue(); cache.put(key, value); @@ -643,7 +654,7 @@ public void put_replace_sameValue(Cache cache, CacheContext context) { public void put_replace_sameInstance(Cache cache, CacheContext context) { var replaced = new HashMap(); for (Int key : context.firstMiddleLastKeys()) { - Int value = context.original().get(key); + Int value = requireNonNull(context.original().get(key)); cache.put(key, value); assertThat(cache).containsEntry(key, value); replaced.put(key, value); @@ -673,6 +684,7 @@ public void put_replace_differentValue(Cache cache, CacheContext conte } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void put_nullKey(Cache cache, CacheContext context) { @@ -680,6 +692,7 @@ public void put_nullKey(Cache cache, CacheContext context) { } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void put_nullValue(Cache cache, CacheContext context) { @@ -687,6 +700,7 @@ public void put_nullValue(Cache cache, CacheContext context) { } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void put_nullKeyAndValue(Cache cache, CacheContext context) { @@ -750,6 +764,7 @@ public void putAll_empty(Cache cache, CacheContext context) { } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void putAll_null(Cache cache, CacheContext context) { @@ -781,6 +796,7 @@ public void invalidate_present(Cache cache, CacheContext context) { } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void invalidate_nullKey(Cache cache, CacheContext context) { @@ -828,6 +844,7 @@ public void invalidateAll_full(Cache cache, CacheContext context) { @CacheSpec @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") public void invalidateAll_null(Cache cache, CacheContext context) { assertThrows(NullPointerException.class, () -> cache.invalidateAll(null)); @@ -860,7 +877,7 @@ public void invalidateAll_removalListener_writeback(Cache cache, Cache cache.invalidateAll(); assertThat(context).removalNotifications().withCause(EXPLICIT) .contains(context.original()).exclusively(); - assertThat(cache).containsExactlyEntriesIn(Maps.asMap(context.original().keySet(), key -> key)); + assertThat(cache).containsExactlyEntriesIn(Maps.toMap(context.original().keySet(), key -> key)); } /* --------------- cleanup --------------- */ @@ -902,6 +919,7 @@ public void removalListener_submit_error_log(Cache cache, CacheContext } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, refreshAfterWrite = Expire.DISABLED, expireAfterAccess = Expire.DISABLED, expireAfterWrite = Expire.DISABLED, @@ -1040,6 +1058,7 @@ public void stats(Cache cache, CacheContext context) { /* --------------- Policy: getIfPresentQuietly --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getIfPresentQuietly_nullKey(Cache cache, CacheContext context) { @@ -1066,6 +1085,7 @@ public void getIfPresentQuietly_present(Cache cache, CacheContext cont /* --------------- Policy: getEntryIfPresentQuietly --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getEntryIfPresentQuietly_nullKey(Cache cache, CacheContext context) { @@ -1085,7 +1105,7 @@ public void getEntryIfPresentQuietly_absent(Cache cache, CacheContext removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getEntryIfPresentQuietly_present(Cache cache, CacheContext context) { for (Int key : context.firstMiddleLastKeys()) { - var entry = cache.policy().getEntryIfPresentQuietly(key); + var entry = requireNonNull(cache.policy().getEntryIfPresentQuietly(key)); assertThat(context).containsEntry(entry); } } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineSpecGuavaTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineSpecGuavaTest.java index aa2b2fc4dd..4b01a97910 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineSpecGuavaTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineSpecGuavaTest.java @@ -354,21 +354,25 @@ public void testEqualsAndHashCode() { .testEquals(); } + @SuppressWarnings("NullAway") public void testMaximumWeight_withWeigher() { Caffeine builder = Caffeine.from(parse("maximumWeight=9000")); assertThat(builder.weigher((k, v) -> 42).build(k -> null)).isNotNull(); } + @SuppressWarnings("NullAway") public void testMaximumWeight_withoutWeigher() { Caffeine builder = Caffeine.from(parse("maximumWeight=9000")); assertThrows(IllegalStateException.class, () -> builder.build(k -> null)); } + @SuppressWarnings("NullAway") public void testMaximumSize_withWeigher() { Caffeine builder = Caffeine.from(parse("maximumSize=9000")); assertThat(builder.weigher((k, v) -> 42).build(k -> null)).isNotNull(); } + @SuppressWarnings("NullAway") public void testMaximumSize_withoutWeigher() { Caffeine builder = Caffeine.from(parse("maximumSize=9000")); assertThat(builder.build(k -> null)).isNotNull(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineTest.java index 7bd8d5d09f..7b5edb7131 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/CaffeineTest.java @@ -28,11 +28,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.Mockito; import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.stats.StatsCounter; @@ -56,16 +53,12 @@ * @author ben.manes@gmail.com (Ben Manes) */ public final class CaffeineTest { - @Mock StatsCounter statsCounter; - @Mock Expiry expiry; - @Mock CacheLoader loader; + private static final Expiry expiry = Expiry.accessing( + (key, value) -> { throw new AssertionError(); }); + private static final CacheLoader loader = + key -> { throw new AssertionError(); }; - @BeforeClass - public void beforeClass() throws Exception { - MockitoAnnotations.openMocks(this).close(); - } - - @BeforeMethod @AfterMethod + @AfterMethod public void reset() { TestLoggerFactory.clear(); } @@ -104,6 +97,7 @@ public void configured() { } @Test + @SuppressWarnings("NullAway") public void fromSpec_null() { assertThrows(NullPointerException.class, () -> Caffeine.from((CaffeineSpec) null)); } @@ -126,6 +120,7 @@ public void fromSpec() { } @Test + @SuppressWarnings("NullAway") public void fromString_null() { assertThrows(NullPointerException.class, () -> Caffeine.from((String) null)); } @@ -196,6 +191,7 @@ public void calculateHashMapCapacity() { /* --------------- loading --------------- */ @Test + @SuppressWarnings("NullAway") public void loading_nullLoader() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().build(null)); } @@ -224,6 +220,7 @@ public void async_weakKeys_evictionListener() { /* --------------- async loader --------------- */ @Test + @SuppressWarnings("NullAway") public void asyncLoader_nullLoader() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().buildAsync((CacheLoader) null)); @@ -377,6 +374,7 @@ public void maximumWeight_large() { /* --------------- weigher --------------- */ @Test + @SuppressWarnings("NullAway") public void weigher_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().weigher(null)); } @@ -589,6 +587,7 @@ public void expireAfterWrite_duration_excessive() { /* --------------- expiry --------------- */ @Test + @SuppressWarnings("NullAway") public void expireAfter_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().expireAfter(null)); } @@ -744,6 +743,7 @@ public void softValues() { /* --------------- scheduler --------------- */ @Test + @SuppressWarnings("NullAway") public void scheduler_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().scheduler(null)); } @@ -764,7 +764,7 @@ public void scheduler_system() { @Test public void scheduler_custom() { - Scheduler scheduler = (executor, task, delay, unit) -> DisabledFuture.INSTANCE; + Scheduler scheduler = (executor, task, delay, unit) -> DisabledFuture.instance(); var builder = Caffeine.newBuilder().scheduler(scheduler); assertThat(((GuardedScheduler) builder.getScheduler()).delegate).isSameInstanceAs(scheduler); assertThat(builder.build()).isNotNull(); @@ -773,6 +773,7 @@ public void scheduler_custom() { /* --------------- executor --------------- */ @Test + @SuppressWarnings("NullAway") public void executor_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().executor(null)); } @@ -793,6 +794,7 @@ public void executor() { /* --------------- ticker --------------- */ @Test + @SuppressWarnings("NullAway") public void ticker_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().ticker(null)); } @@ -815,6 +817,7 @@ public void ticker() { /* --------------- stats --------------- */ @Test + @SuppressWarnings("NullAway") public void recordStats_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().recordStats(null)); } @@ -822,7 +825,7 @@ public void recordStats_null() { @Test public void recordStats_twice() { var type = IllegalStateException.class; - Supplier supplier = () -> statsCounter; + Supplier supplier = Mockito::mock; assertThrows(type, () -> Caffeine.newBuilder().recordStats().recordStats()); assertThrows(type, () -> Caffeine.newBuilder().recordStats(supplier).recordStats()); assertThrows(type, () -> Caffeine.newBuilder().recordStats().recordStats(supplier)); @@ -838,9 +841,19 @@ public void recordStats() { @Test public void recordStats_custom() { - Supplier supplier = () -> statsCounter; - var builder = Caffeine.newBuilder().recordStats(supplier); - builder.statsCounterSupplier.get().recordEviction(1, RemovalCause.SIZE); + StatsCounter statsCounter = Mockito.mock(); + var builder = Caffeine.newBuilder().recordStats(() -> statsCounter); + assertThat(builder.statsCounterSupplier).isNotNull(); + + var counter1 = builder.statsCounterSupplier.get(); + var counter2 = builder.statsCounterSupplier.get(); + assertThat(counter1).isNotSameInstanceAs(counter2); + assertThat(counter1).isNotNull(); + assertThat(counter2).isNotNull(); + + assertThat(counter1.getClass().getName()) + .isEqualTo("com.github.benmanes.caffeine.cache.stats.GuardedStatsCounter"); + counter1.recordEviction(1, RemovalCause.SIZE); verify(statsCounter).recordEviction(1, RemovalCause.SIZE); assertThat(builder.build()).isNotNull(); } @@ -848,6 +861,7 @@ public void recordStats_custom() { /* --------------- removalListener --------------- */ @Test + @SuppressWarnings("NullAway") public void removalListener_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().removalListener(null)); } @@ -869,6 +883,7 @@ public void removalListener() { /* --------------- evictionListener --------------- */ @Test + @SuppressWarnings("NullAway") public void evictionListener_null() { assertThrows(NullPointerException.class, () -> Caffeine.newBuilder().evictionListener(null)); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/EvictionTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/EvictionTest.java index ca451325d4..e72fe319fe 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/EvictionTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/EvictionTest.java @@ -1199,12 +1199,14 @@ public void coldest_snapshot(Cache cache, assertThat(coldest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(initialCapacity = InitialCapacity.EXCESSIVE, maximumSize = Maximum.FULL) public void coldestFunc_null(CacheContext context, Eviction eviction) { assertThrows(NullPointerException.class, () -> eviction.coldest(null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(initialCapacity = InitialCapacity.EXCESSIVE, maximumSize = Maximum.FULL) public void coldestFunc_nullResult(CacheContext context, Eviction eviction) { @@ -1404,12 +1406,14 @@ public void hottest_snapshot(Cache cache, assertThat(hottest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(initialCapacity = InitialCapacity.EXCESSIVE, maximumSize = Maximum.FULL) public void hottestFunc_null(CacheContext context, Eviction eviction) { assertThrows(NullPointerException.class, () -> eviction.hottest(null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(initialCapacity = InitialCapacity.EXCESSIVE, maximumSize = Maximum.FULL) public void hottestFunc_nullResult(CacheContext context, Eviction eviction) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpirationTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpirationTest.java index 4342d0f82a..f66aa1c8a7 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpirationTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpirationTest.java @@ -142,7 +142,7 @@ public void schedule(Cache cache, CacheContext context) { var delay = ArgumentCaptor.forClass(long.class); var task = ArgumentCaptor.forClass(Runnable.class); when(context.scheduler().schedule(eq(context.executor()), task.capture(), delay.capture(), - eq(TimeUnit.NANOSECONDS))).then(invocation -> DisabledFuture.INSTANCE); + eq(TimeUnit.NANOSECONDS))).then(invocation -> DisabledFuture.instance()); cache.put(context.absentKey(), context.absentValue()); @@ -556,10 +556,10 @@ public void getAll(AsyncCache cache, CacheContext context) { var keys = context.firstMiddleLastKeys(); context.ticker().advance(Duration.ofMinutes(1)); cache.getAll(context.firstMiddleLastKeys(), - keysToLoad -> Maps.asMap(keysToLoad, identity())).join(); + keysToLoad -> Maps.toMap(keysToLoad, identity())).join(); - var expected = Maps.asMap(keys, identity()); - assertThat(cache.getAll(keys, keysToLoad -> Maps.asMap(keysToLoad, identity())).join()) + var expected = Maps.toMap(keys, identity()); + assertThat(cache.getAll(keys, keysToLoad -> Maps.toMap(keysToLoad, identity())).join()) .containsExactlyEntriesIn(expected).inOrder(); assertThat(context).notifications().withCause(EXPIRED) .contains(context.original()).exclusively(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterAccessTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterAccessTest.java index 86d6486490..85dffcc2fd 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterAccessTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterAccessTest.java @@ -21,11 +21,13 @@ import static com.github.benmanes.caffeine.cache.testing.CacheSpec.Expiration.AFTER_ACCESS; import static com.github.benmanes.caffeine.cache.testing.CacheSpec.Expiration.VARIABLE; import static com.github.benmanes.caffeine.cache.testing.CacheSubject.assertThat; +import static com.github.benmanes.caffeine.testing.FutureSubject.assertThat; import static com.github.benmanes.caffeine.testing.MapSubject.assertThat; import static com.google.common.base.Functions.identity; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertThrows; import static org.slf4j.event.Level.WARN; @@ -97,7 +99,7 @@ public void getIfPresent(Cache cache, CacheContext context) { expiry = { CacheExpiry.DISABLED, CacheExpiry.ACCESS }, expiryTime = Expire.ONE_MINUTE, expireAfterAccess = Expire.ONE_MINUTE, population = { Population.PARTIAL, Population.FULL }) public void get(Cache cache, CacheContext context) { - Function mappingFunction = context.original()::get; + Function mappingFunction = key -> requireNonNull(context.original().get(key)); context.ticker().advance(Duration.ofSeconds(30)); var value1 = cache.get(context.firstKey(), mappingFunction); assertThat(value1).isEqualTo(context.original().get(context.firstKey())); @@ -146,21 +148,21 @@ public void getAllPresent(Cache cache, CacheContext context) { public void getAll(Cache cache, CacheContext context) { context.ticker().advance(Duration.ofSeconds(30)); var result1 = cache.getAll(List.of(context.firstKey(), context.middleKey()), - keys -> Maps.asMap(keys, identity())); + keys -> Maps.toMap(keys, identity())); assertThat(result1).containsExactly(context.firstKey(), context.firstKey().negate(), context.middleKey(), context.middleKey().negate()); context.ticker().advance(Duration.ofSeconds(45)); cache.cleanUp(); var result2 = cache.getAll(List.of(context.firstKey(), context.absentKey()), - keys -> Maps.asMap(keys, identity())); + keys -> Maps.toMap(keys, identity())); assertThat(result2).containsExactly(context.firstKey(), context.firstKey().negate(), context.absentKey(), context.absentKey()); context.ticker().advance(Duration.ofSeconds(45)); cache.cleanUp(); var result3 = cache.getAll(List.of(context.middleKey(), context.absentKey()), - keys -> Maps.asMap(keys, identity())); + keys -> Maps.toMap(keys, identity())); assertThat(result3).containsExactly(context.middleKey(), context.middleKey(), context.absentKey(), context.absentKey()); assertThat(cache).hasSize(3); @@ -233,7 +235,9 @@ public void getAll_loading(LoadingCache cache, CacheContext context) { expireAfterAccess = Expire.ONE_MINUTE, population = { Population.PARTIAL, Population.FULL }) public void getIfPresent_async(AsyncCache cache, CacheContext context) { context.ticker().advance(Duration.ofSeconds(30)); - cache.getIfPresent(context.firstKey()).join(); + assertThat(cache.getIfPresent(context.firstKey())) + .succeedsWith(context.original().get(context.firstKey())); + context.ticker().advance(Duration.ofSeconds(45)); assertThat(cache).containsKey(context.firstKey()); assertThat(cache).doesNotContainKey(context.lastKey()); @@ -433,6 +437,7 @@ public void oldest_snapshot(Cache cache, CacheContext context, assertThat(oldest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expireAfterAccess = Expire.ONE_MINUTE) public void oldestFunc_null(CacheContext context, @@ -440,6 +445,7 @@ public void oldestFunc_null(CacheContext context, assertThrows(NullPointerException.class, () -> expireAfterAccess.oldest(null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expireAfterAccess = Expire.ONE_MINUTE) public void oldestFunc_nullResult(CacheContext context, @@ -582,6 +588,7 @@ public void youngest_snapshot(Cache cache, CacheContext context, assertThat(youngest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expireAfterAccess = Expire.ONE_MINUTE) public void youngestFunc_null(CacheContext context, @@ -589,6 +596,7 @@ public void youngestFunc_null(CacheContext context, assertThrows(NullPointerException.class, () -> expireAfterAccess.youngest(null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expireAfterAccess = Expire.ONE_MINUTE) public void youngestFunc_nullResult(CacheContext context, diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java index d447b056c6..8c2579cf71 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java @@ -34,6 +34,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -57,6 +58,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; import org.mockito.Mockito; import org.testng.annotations.Listeners; import org.testng.annotations.Test; @@ -961,6 +963,7 @@ public void setExpiresAfter_expired(Cache cache, /* --------------- Policy: putIfAbsent --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -971,6 +974,7 @@ public void putIfAbsent_nullKey(Cache cache, } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -981,6 +985,7 @@ public void putIfAbsent_nullValue(Cache cache, } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -1001,6 +1006,7 @@ public void putIfAbsent_negativeDuration(Cache cache, } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -1062,6 +1068,7 @@ public void putIfAbsent_present(Cache cache, /* --------------- Policy: put --------------- */ @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -1072,6 +1079,7 @@ public void put_nullKey(Cache cache, } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -1082,6 +1090,7 @@ public void put_nullValue(Cache cache, } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -1115,6 +1124,7 @@ public void put_excessiveDuration(Cache cache, } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.WRITE, expiryTime = Expire.ONE_MINUTE) @@ -1162,6 +1172,7 @@ public void put_replace(Cache cache, /* --------------- Policy: compute --------------- */ + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expiry = CacheExpiry.ACCESS, removalListener = {Listener.DISABLED, Listener.REJECTING}) public void compute_nullKey(CacheContext context, VarExpiration expireAfterVar) { @@ -1170,6 +1181,7 @@ public void compute_nullKey(CacheContext context, VarExpiration expire } @CheckNoStats + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expiry = CacheExpiry.ACCESS, removalListener = {Listener.DISABLED, Listener.REJECTING}) public void compute_nullMappingFunction(CacheContext context, @@ -1227,8 +1239,8 @@ public void compute_remove(Cache cache, @SuppressWarnings("CheckReturnValue") @CacheSpec(expiry = CacheExpiry.ACCESS) public void compute_recursive(CacheContext context, VarExpiration expireAfterVar) { - var mappingFunction = new BiFunction() { - @Override public Int apply(Int key, Int value) { + var mappingFunction = new BiFunction() { + @Override public @Nullable Int apply(Int key, Int value) { return expireAfterVar.compute(key, this, Duration.ofDays(1)); } }; @@ -1243,8 +1255,8 @@ public void compute_recursive(CacheContext context, VarExpiration expi public void compute_pingpong(CacheContext context, VarExpiration expireAfterVar) { var key1 = Int.valueOf(1); var key2 = Int.valueOf(2); - var mappingFunction = new BiFunction() { - @Override public Int apply(Int key, Int value) { + var mappingFunction = new BiFunction() { + @Override public @Nullable Int apply(Int key, Int value) { return expireAfterVar.compute(key.equals(key1) ? key2 : key1, this, Duration.ofDays(1)); } }; @@ -1416,7 +1428,7 @@ public void compute_differentValue(Cache cache, var replaced = new HashMap(); var duration = context.expiryTime().duration().dividedBy(2); for (Int key : context.firstMiddleLastKeys()) { - Int value = context.original().get(key); + Int value = requireNonNull(context.original().get(key)); Int result = expireAfterVar.compute(key, (k, v) -> value.negate(), duration); replaced.put(key, value); @@ -1679,12 +1691,14 @@ public void oldest_snapshot(Cache cache, assertThat(oldest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expiry = CacheExpiry.ACCESS) public void oldestFunc_null(CacheContext context, VarExpiration expireAfterVar) { assertThrows(NullPointerException.class, () -> expireAfterVar.oldest(null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expiry = CacheExpiry.ACCESS) public void oldestFunc_nullResult(CacheContext context, VarExpiration expireAfterVar) { @@ -1819,12 +1833,14 @@ public void youngest_snapshot(Cache cache, assertThat(youngest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expiry = CacheExpiry.ACCESS) public void youngestFunc_null(CacheContext context, VarExpiration expireAfterVar) { assertThrows(NullPointerException.class, () -> expireAfterVar.youngest(null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expiry = CacheExpiry.ACCESS) public void youngestFunc_nullResult(CacheContext context, diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java index 2f3ee7914f..a13a8c190b 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java @@ -27,6 +27,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertThrows; import static org.slf4j.event.Level.WARN; @@ -94,7 +95,7 @@ public void getIfPresent(Cache cache, CacheContext context) { mustExpireWithAnyOf = { AFTER_WRITE, VARIABLE }, expireAfterWrite = Expire.ONE_MINUTE, expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE }, expiryTime = Expire.ONE_MINUTE) public void get(Cache cache, CacheContext context) { - Function mappingFunction = context.original()::get; + Function mappingFunction = key -> requireNonNull(context.original().get(key)); context.ticker().advance(Duration.ofSeconds(30)); var value = cache.get(context.firstKey(), mappingFunction); assertThat(value).isEqualTo(context.original().get(context.firstKey())); @@ -135,14 +136,14 @@ public void getAllPresent(Cache cache, CacheContext context) { public void getAll(Cache cache, CacheContext context) { context.ticker().advance(Duration.ofSeconds(30)); var result1 = cache.getAll(List.of(context.firstKey(), context.absentKey()), - keys -> Maps.asMap(keys, identity())); + keys -> Maps.toMap(keys, identity())); assertThat(result1).containsExactly( context.firstKey(), context.firstKey().negate(), context.absentKey(), context.absentKey()); context.ticker().advance(Duration.ofSeconds(45)); cache.cleanUp(); var result2 = cache.getAll(List.of(context.firstKey(), context.absentKey()), - keys -> Maps.asMap(keys, identity())); + keys -> Maps.toMap(keys, identity())); assertThat(result2).containsExactly( context.firstKey(), context.firstKey(), context.absentKey(), context.absentKey()); assertThat(cache).hasSize(2); @@ -395,6 +396,7 @@ public void oldest_snapshot(Cache cache, CacheContext context, assertThat(oldest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expireAfterWrite = Expire.ONE_MINUTE) public void oldestFunc_null(CacheContext context, @@ -406,6 +408,7 @@ public void oldestFunc_null(CacheContext context, @CacheSpec(expireAfterWrite = Expire.ONE_MINUTE) public void oldestFunc_nullResult(CacheContext context, @ExpireAfterWrite FixedExpiration expireAfterWrite) { + @SuppressWarnings("NullAway") var result = expireAfterWrite.oldest(stream -> null); assertThat(result).isNull(); } @@ -544,6 +547,7 @@ public void youngest_snapshot(Cache cache, CacheContext context, assertThat(youngest).containsExactlyEntriesIn(context.original()); } + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(expireAfterWrite = Expire.ONE_MINUTE) public void youngestFunc_null(CacheContext context, @@ -555,6 +559,7 @@ public void youngestFunc_null(CacheContext context, @CacheSpec(expireAfterWrite = Expire.ONE_MINUTE) public void youngestFunc_nullResult(CacheContext context, @ExpireAfterWrite FixedExpiration expireAfterWrite) { + @SuppressWarnings("NullAway") var result = expireAfterWrite.youngest(stream -> null); assertThat(result).isNull(); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/InternerTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/InternerTest.java index 662caaa84c..c72e9fad5f 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/InternerTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/InternerTest.java @@ -65,6 +65,7 @@ public static TestSuite suite() { .createTestSuite(); } + @SuppressWarnings("NullAway") @Test(dataProvider = "interners") public void intern_null(Interner interner) { assertThrows(NullPointerException.class, () -> interner.intern(null)); @@ -143,6 +144,7 @@ public void nullPointerExceptions() { } @Test + @SuppressWarnings("NullAway") public void factory() { assertThat(Interned.FACTORY.newReferenceKey(new Object(), null)) .isInstanceOf(WeakKeyEqualsReference.class); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeSubject.java index 209b281e48..f1766c5e76 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeSubject.java @@ -16,9 +16,12 @@ package com.github.benmanes.caffeine.cache; import static com.google.common.truth.Truth.assertAbout; +import static java.util.Objects.requireNonNull; import java.util.Iterator; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.testing.CollectionSubject; import com.google.common.collect.Sets; import com.google.common.truth.FailureMetadata; @@ -32,9 +35,10 @@ final class LinkedDequeSubject extends CollectionSubject { private final LinkedDeque actual; @SuppressWarnings("unchecked") - private LinkedDequeSubject(FailureMetadata metadata, LinkedDeque subject) { + private LinkedDequeSubject(FailureMetadata metadata, @Nullable LinkedDeque subject) { super(metadata, subject); - this.actual = (LinkedDeque) subject; + this.actual = requireNonNull((LinkedDeque) subject); + } public static Factory> deque() { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTest.java index 20eabf84f2..a5e8b2d8db 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTest.java @@ -18,6 +18,7 @@ import static com.github.benmanes.caffeine.testing.CollectionSubject.assertThat; import static com.google.common.collect.Iterators.elementsEqual; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertThrows; import java.util.ArrayList; @@ -29,6 +30,7 @@ import java.util.NoSuchElementException; import java.util.concurrent.ThreadLocalRandom; +import org.jspecify.annotations.Nullable; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -115,7 +117,9 @@ public void moveToFront_last(LinkedDeque deque) { checkMoveToFront(deque, deque.getLast()); } - private static void checkMoveToFront(LinkedDeque deque, LinkedValue element) { + private static void checkMoveToFront(LinkedDeque deque, + @Nullable LinkedValue element) { + assertThat(element).isNotNull(); deque.moveToFront(element); assertThat(deque).hasSize(SIZE); assertThat(deque.peekFirst()).isEqualTo(element); @@ -136,7 +140,9 @@ public void moveToBack_last(LinkedDeque deque) { checkMoveToBack(deque, deque.getLast()); } - private static void checkMoveToBack(LinkedDeque deque, LinkedValue element) { + private static void checkMoveToBack(LinkedDeque deque, + @Nullable LinkedValue element) { + assertThat(element).isNotNull(); deque.moveToBack(element); assertThat(deque).hasSize(SIZE); assertThat(deque.getLast()).isEqualTo(element); @@ -152,7 +158,7 @@ public void isFirst_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void isFirst_whenPopulated(AbstractLinkedDeque deque) { - var first = deque.first; + var first = requireNonNull(deque.first); assertThat(deque).hasSize(SIZE); assertThat(deque.isFirst(first)).isTrue(); assertThat(deque.contains(first)).isTrue(); @@ -167,7 +173,7 @@ public void isLast_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void isLast_whenPopulated(AbstractLinkedDeque deque) { - var last = deque.last; + var last = requireNonNull(deque.last); assertThat(deque).hasSize(SIZE); assertThat(deque.isLast(last)).isTrue(); assertThat(deque.contains(last)).isTrue(); @@ -183,7 +189,7 @@ public void peek_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void peek_whenPopulated(AbstractLinkedDeque deque) { - var first = deque.first; + var first = requireNonNull(deque.first); assertThat(deque).hasSize(SIZE); assertThat(deque.contains(first)).isTrue(); assertThat(deque.first).isSameInstanceAs(first); @@ -197,7 +203,7 @@ public void peekFirst_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void peekFirst_whenPopulated(AbstractLinkedDeque deque) { - var first = deque.first; + var first = requireNonNull(deque.first); assertThat(deque).hasSize(SIZE); assertThat(deque.contains(first)).isTrue(); assertThat(deque.first).isSameInstanceAs(first); @@ -211,7 +217,7 @@ public void peekLast_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void peekLast_whenPopulated(AbstractLinkedDeque deque) { - var last = deque.last; + var last = requireNonNull(deque.last); assertThat(deque).hasSize(SIZE); assertThat(deque.contains(last)).isTrue(); assertThat(deque.last).isSameInstanceAs(last); @@ -227,7 +233,7 @@ public void getFirst_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void getFirst_whenPopulated(AbstractLinkedDeque deque) { - var first = deque.first; + var first = requireNonNull(deque.first); assertThat(deque).hasSize(SIZE); assertThat(deque.contains(first)).isTrue(); assertThat(deque.first).isSameInstanceAs(first); @@ -241,7 +247,7 @@ public void getLast_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void getLast_whenPopulated(AbstractLinkedDeque deque) { - var last = deque.last; + var last = requireNonNull(deque.last); assertThat(deque).hasSize(SIZE); assertThat(deque.contains(last)).isTrue(); assertThat(deque.last).isSameInstanceAs(last); @@ -257,7 +263,7 @@ public void element_whenEmpty(LinkedDeque deque) { @Test(dataProvider = "full") public void element_whenPopulated(AbstractLinkedDeque deque) { - var first = deque.first; + var first = requireNonNull(deque.first); assertThat(deque).hasSize(SIZE); assertThat(deque.contains(first)).isTrue(); assertThat(deque.first).isSameInstanceAs(first); @@ -862,34 +868,34 @@ void populate(Collection collection) { static final class LinkedValue implements AccessOrder, WriteOrder { final int value; - LinkedValue prev; - LinkedValue next; + @Nullable LinkedValue prev; + @Nullable LinkedValue next; LinkedValue(int value) { this.value = value; } - @Override public LinkedValue getPreviousInAccessOrder() { + @Override public @Nullable LinkedValue getPreviousInAccessOrder() { return prev; } - @Override public void setPreviousInAccessOrder(LinkedValue prev) { + @Override public void setPreviousInAccessOrder(@Nullable LinkedValue prev) { this.prev = prev; } - @Override public LinkedValue getNextInAccessOrder() { + @Override public @Nullable LinkedValue getNextInAccessOrder() { return next; } - @Override public void setNextInAccessOrder(LinkedValue next) { + @Override public void setNextInAccessOrder(@Nullable LinkedValue next) { this.next = next; } - @Override public LinkedValue getPreviousInWriteOrder() { + @Override public @Nullable LinkedValue getPreviousInWriteOrder() { return prev; } - @Override public void setPreviousInWriteOrder(LinkedValue prev) { + @Override public void setPreviousInWriteOrder(@Nullable LinkedValue prev) { this.prev = prev; } - @Override public LinkedValue getNextInWriteOrder() { + @Override public @Nullable LinkedValue getNextInWriteOrder() { return next; } - @Override public void setNextInWriteOrder(LinkedValue next) { + @Override public void setNextInWriteOrder(@Nullable LinkedValue next) { this.next = next; } @Override public boolean equals(Object o) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTests.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTests.java index 38c579cf53..406319bb44 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTests.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LinkedDequeTests.java @@ -128,7 +128,7 @@ static final class LinkedValue implements AccessOrder, WriteOrder cache, CacheContext context) { @@ -160,6 +162,7 @@ public void get_present(LoadingCache cache, CacheContext context) { /* --------------- getAll --------------- */ @CheckNoEvictions + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void getAll_iterable_null(LoadingCache cache, CacheContext context) { @@ -433,13 +436,14 @@ final class Key { return 0; // to put keys in one bucket } } + @SuppressWarnings("NullAway") LoadingCache cache = context.build(key -> null); var keys = intern(new ArrayList()); for (int i = 0; i < Population.FULL.size(); i++) { keys.add(new Key()); } - Key key = Iterables.getLast(keys); + Key key = requireNonNull(Iterables.getLast(keys)); Int value = context.absentValue(); cache.put(key, value); @@ -451,6 +455,7 @@ final class Key { /* --------------- refresh --------------- */ @CheckNoEvictions + @SuppressWarnings("NullAway") @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void refresh_null(LoadingCache cache, CacheContext context) { @@ -958,6 +963,7 @@ public void refresh_nullFuture_load(CacheContext context) { @Override public Int load(Int key) { throw new IllegalStateException(); } + @SuppressWarnings("NullAway") @Override public CompletableFuture asyncLoad(Int key, Executor executor) { return null; } @@ -972,6 +978,7 @@ public void refresh_nullFuture_reload(CacheContext context) { @Override public Int load(Int key) { throw new IllegalStateException(); } + @SuppressWarnings("NullAway") @Override public CompletableFuture asyncReload( Int key, Int oldValue, Executor executor) { return null; @@ -984,6 +991,7 @@ public void refresh_nullFuture_reload(CacheContext context) { /* --------------- refreshAll --------------- */ @Test(dataProvider = "caches") + @SuppressWarnings("NullAway") @CheckNoEvictions @CheckNoStats @CacheSpec(removalListener = { Listener.DISABLED, Listener.REJECTING }) public void refreshAll_null(LoadingCache cache, CacheContext context) { @@ -1085,6 +1093,7 @@ public void refreshAll_nullFuture_load(CacheContext context) { @Override public Int load(Int key) { throw new IllegalStateException(); } + @SuppressWarnings("NullAway") @Override public CompletableFuture asyncLoad(Int key, Executor executor) { return null; } @@ -1099,6 +1108,7 @@ public void refreshAll_nullFuture_reload(CacheContext context) { @Override public Int load(Int key) { throw new IllegalStateException(); } + @SuppressWarnings("NullAway") @Override public CompletableFuture asyncReload( Int key, Int oldValue, Executor executor) { return null; @@ -1112,7 +1122,7 @@ public void refreshAll_nullFuture_reload(CacheContext context) { @Test public void loadAll() { - CacheLoader loader = key -> key; + CacheLoader loader = key -> key; assertThrows(UnsupportedOperationException.class, () -> loader.loadAll(Set.of())); } @@ -1153,7 +1163,7 @@ public void asyncLoadAll_exception() throws Exception { @Test public void asyncLoadAll() throws Throwable { - CacheLoader loader = key -> key; + CacheLoader loader = key -> key; assertThat(loader.asyncLoadAll(Set.of(), Runnable::run)) .failsWith(CompletionException.class) .hasCauseThat().isInstanceOf(UnsupportedOperationException.class); @@ -1176,6 +1186,7 @@ public void asyncReload() throws Exception { } @Test + @SuppressWarnings("NullAway") public void bulk_null() { assertThrows(NullPointerException.class, () -> CacheLoader.bulk(null)); } @@ -1201,9 +1212,9 @@ public void bulk_present() throws Exception { @Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, loader = Loader.ASYNC_INCOMPLETE) public void refreshes(LoadingCache cache, CacheContext context) { - var key1 = Iterables.get(context.absentKeys(), 0); + var key1 = requireNonNull(Iterables.get(context.absentKeys(), 0)); var key2 = context.original().isEmpty() - ? Iterables.get(context.absentKeys(), 1) + ? requireNonNull(Iterables.get(context.absentKeys(), 1)) : context.firstKey(); var future1 = cache.refresh(key1); var future2 = cache.refresh(key2); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LocalCacheSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LocalCacheSubject.java index e4163ad913..13890f5654 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LocalCacheSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LocalCacheSubject.java @@ -19,6 +19,7 @@ import static com.github.benmanes.caffeine.testing.Awaits.await; import static com.github.benmanes.caffeine.testing.MapSubject.map; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import java.util.Collection; import java.util.Map; @@ -26,6 +27,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.Async.AsyncWeigher; import com.github.benmanes.caffeine.cache.BoundedLocalCache.BoundedLocalAsyncCache; import com.github.benmanes.caffeine.cache.BoundedLocalCache.BoundedLocalAsyncLoadingCache; @@ -38,7 +41,7 @@ import com.github.benmanes.caffeine.cache.UnboundedLocalCache.UnboundedLocalAsyncLoadingCache; import com.github.benmanes.caffeine.cache.UnboundedLocalCache.UnboundedLocalManualCache; import com.github.benmanes.caffeine.cache.stats.StatsCounter; -import com.github.benmanes.caffeine.cache.testing.Weighers; +import com.github.benmanes.caffeine.cache.testing.Weighers.SkippedWeigher; import com.google.common.collect.ImmutableTable; import com.google.common.collect.Sets; import com.google.common.truth.FailureMetadata; @@ -54,9 +57,9 @@ public final class LocalCacheSubject extends Subject { private final Object actual; - private LocalCacheSubject(FailureMetadata metadata, Object subject) { + private LocalCacheSubject(FailureMetadata metadata, @Nullable Object subject) { super(metadata, subject); - this.actual = subject; + this.actual = requireNonNull(subject); } public static Factory> asyncLocal() { @@ -298,11 +301,12 @@ private void checkLinks(BoundedLocalCache bounded, long totalWeightedSize = 0; Set> seen = Sets.newIdentityHashSet(); for (var cell : deques.cellSet()) { - long weightedSize = scanLinks(bounded, cell.getValue(), seen); - check("%s: %s in %s", cell.getRowKey(), cell.getValue(), bounded.data) + var deque = requireNonNull(cell.getValue()); + long weightedSize = scanLinks(bounded, deque, seen); + check("%s: %s in %s", cell.getRowKey(), deque, bounded.data) .that(weightedSize).isEqualTo(cell.getColumnKey()); - totalSize += cell.getValue().size(); totalWeightedSize += weightedSize; + totalSize += deque.size(); } check("linkSize").withMessage("cache.size() != links").that(bounded).hasSize(seen.size()); check("totalSize").withMessage("cache.size() == deque.size()").that(bounded).hasSize(totalSize); @@ -354,7 +358,7 @@ private void checkNode(BoundedLocalCache bounded, Node bounded, - Node node, Object key, Object value) { + Node node, @Nullable Object key, @Nullable Object value) { if (bounded.collectKeys()) { if ((key != null) && (value != null)) { check("bounded").that(bounded).containsKey(key); @@ -368,10 +372,11 @@ private void checkKey(BoundedLocalCache bounded, } private void checkValue(BoundedLocalCache bounded, - Node node, Object key, Object value) { + Node node, @Nullable Object key, @Nullable Object value) { if (!bounded.collectValues()) { check("value").that(value).isNotNull(); if ((key != null) && !bounded.hasExpired(node, bounded.expirationTicker().read())) { + requireNonNull(value); check("containsValue(value) for key %s", key) .about(map()).that(bounded).containsValue(value); } @@ -379,7 +384,7 @@ private void checkValue(BoundedLocalCache bounded, checkIfAsyncValue(value); } - private void checkIfAsyncValue(Object value) { + private void checkIfAsyncValue(@Nullable Object value) { if (value instanceof CompletableFuture) { var future = (CompletableFuture) value; if (!future.isDone() || future.isCompletedExceptionally()) { @@ -390,18 +395,22 @@ private void checkIfAsyncValue(Object value) { } private void checkWeight(BoundedLocalCache bounded, - Node node, Object key, Object value) { + Node node, @Nullable Object key, @Nullable Object value) { check("node.getWeight").that(node.getWeight()).isAtLeast(0); + if ((key == null) || (value == null)) { + return; + } - var weigher = bounded.weigher; - boolean canCheckWeight = (weigher == Weighers.random()); + Weigher weigher = bounded.weigher; if (weigher instanceof AsyncWeigher) { - @SuppressWarnings("rawtypes") - var asyncWeigher = (AsyncWeigher) weigher; - canCheckWeight = (asyncWeigher.delegate == Weighers.random()); + weigher = ((AsyncWeigher) weigher).delegate; + } + if (weigher instanceof BoundedWeigher) { + weigher = ((BoundedWeigher) weigher).delegate; } - if (canCheckWeight) { - check("node.getWeight()").that(node.getWeight()).isEqualTo(weigher.weigh(key, value)); + if (!(weigher instanceof SkippedWeigher)) { + int weight = bounded.weigher.weigh(key, value); + check("node.getWeight()").that(node.getWeight()).isEqualTo(weight); } } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/PacerTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/PacerTest.java index 550f1e0f40..8d76b4c9a1 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/PacerTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/PacerTest.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.STRICT_STUBS; import java.util.Random; import java.util.concurrent.CompletableFuture; @@ -28,8 +29,10 @@ import java.util.concurrent.TimeUnit; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.testng.MockitoSettings; +import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.google.common.primitives.Ints; @@ -38,6 +41,8 @@ * @author ben.manes@gmail.com (Ben Manes) */ @Test(singleThreaded = true) +@Listeners(MockitoTestNGListener.class) +@MockitoSettings(strictness = STRICT_STUBS) public final class PacerTest { private static final long ONE_MINUTE_IN_NANOS = TimeUnit.MINUTES.toNanos(1); private static final Random random = new Random(); @@ -51,8 +56,7 @@ public final class PacerTest { Pacer pacer; @BeforeMethod - public void beforeMethod() throws Exception { - MockitoAnnotations.openMocks(this).close(); + public void beforeMethod() { pacer = new Pacer(scheduler); } @@ -210,7 +214,7 @@ public void isScheduled_nullFuture() { @Test public void isScheduled_doneFuture() { - pacer.future = DisabledFuture.INSTANCE; + pacer.future = DisabledFuture.instance(); assertThat(pacer.isScheduled()).isFalse(); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReferenceTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReferenceTest.java index 253b11f3c6..6a75f03686 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReferenceTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReferenceTest.java @@ -30,6 +30,7 @@ import static com.google.common.base.Predicates.not; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -204,8 +205,8 @@ public void getAll(Cache cache, CacheContext context) { context.clear(); GcFinalization.awaitFullGc(); - assertThat(cache.getAll(keys, keysToLoad -> Maps.asMap(keysToLoad, Int::negate))) - .containsExactlyEntriesIn(Maps.asMap(keys, Int::negate)); + assertThat(cache.getAll(keys, keysToLoad -> Maps.toMap(keysToLoad, Int::negate))) + .containsExactlyEntriesIn(Maps.toMap(keys, Int::negate)); assertThat(cache).whenCleanedUp().hasSize(keys.size()); assertThat(context).notifications().withCause(COLLECTED) @@ -299,7 +300,8 @@ public void invalidateAll_iterable(Cache cache, CacheContext context) List> collected; var keys = context.firstMiddleLastKeys(); if (context.isStrongValues()) { - retained = Maps.toMap(context.firstMiddleLastKeys(), key -> context.original().get(key)); + retained = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); collected = getExpectedAfterGc(context, Maps.filterKeys(context.original(), not(keys::contains))); } else { @@ -335,7 +337,8 @@ public void invalidateAll_full(Cache cache, CacheContext context) { List> collected = getExpectedAfterGc(context, Maps.filterKeys(context.original(), not(keys::contains))); if (context.isStrongValues()) { - retained = Maps.toMap(context.firstMiddleLastKeys(), key -> context.original().get(key)); + retained = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); } else { retained = Map.of(); for (var key : keys) { @@ -428,7 +431,7 @@ public void getAll_loading(LoadingCache cache, CacheContext context) { context.clear(); GcFinalization.awaitFullGc(); - assertThat(cache.getAll(keys)).containsExactlyEntriesIn(Maps.asMap(keys, Int::negate)); + assertThat(cache.getAll(keys)).containsExactlyEntriesIn(Maps.toMap(keys, Int::negate)); assertThat(cache).whenCleanedUp().hasSize(keys.size()); assertThat(context).notifications().withCause(COLLECTED) @@ -504,8 +507,8 @@ public void getAll_async(AsyncCache cache, CacheContext context) { context.clear(); GcFinalization.awaitFullGc(); - assertThat(cache.getAll(keys, keysToLoad -> Maps.asMap(keysToLoad, Int::negate)).join()) - .containsExactlyEntriesIn(Maps.asMap(keys, Int::negate)); + assertThat(cache.getAll(keys, keysToLoad -> Maps.toMap(keysToLoad, Int::negate)).join()) + .containsExactlyEntriesIn(Maps.toMap(keys, Int::negate)); assertThat(context).notifications().withCause(COLLECTED) .contains(collected).exclusively(); } @@ -589,7 +592,8 @@ public void containsValue(Map map, CacheContext context) { maximumSize = Maximum.DISABLED, weigher = CacheWeigher.DISABLED, stats = Stats.ENABLED, removalListener = Listener.CONSUMING) public void clear(Map map, CacheContext context) { - var retained = Maps.toMap(context.firstMiddleLastKeys(), key -> context.original().get(key)); + var retained = Maps.toMap(context.firstMiddleLastKeys(), + key -> requireNonNull(context.original().get(key))); var collected = getExpectedAfterGc(context, Maps.difference( context.original(), retained).entriesOnlyOnLeft()); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/RefreshAfterWriteTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/RefreshAfterWriteTest.java index 2be25f8e83..836a45a4c7 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/RefreshAfterWriteTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/RefreshAfterWriteTest.java @@ -30,6 +30,7 @@ import static com.github.benmanes.caffeine.testing.LoggingEvents.logEvents; import static com.github.benmanes.caffeine.testing.MapSubject.assertThat; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThrows; @@ -230,7 +231,7 @@ public void refreshIfNeeded_replaced(LoadingCache cache, CacheContext assertThat(value).isNotNull(); assertThat(cache.policy().refreshes()).isNotEmpty(); - var future = cache.policy().refreshes().get(context.firstKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.firstKey())); cache.put(context.firstKey(), context.absentValue()); future.complete(context.absentKey().negate()); @@ -256,7 +257,7 @@ public void refreshIfNeeded_expired(LoadingCache cache, CacheContext c assertThat(value).isNotNull(); assertThat(cache.policy().refreshes()).isNotEmpty(); - var future = cache.policy().refreshes().get(context.firstKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.firstKey())); context.ticker().advance(Duration.ofMinutes(1)); future.complete(context.absentValue()); @@ -280,7 +281,7 @@ public void refreshIfNeeded_absent_newValue(LoadingCache cache, CacheC assertThat(value).isNotNull(); assertThat(cache.policy().refreshes()).isNotEmpty(); - var future = cache.policy().refreshes().get(context.firstKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.firstKey())); cache.invalidate(context.firstKey()); assertThat(cache).doesNotContainKey(context.firstKey()); @@ -306,7 +307,7 @@ public void refreshIfNeeded_absent_nullValue(LoadingCache cache, Cache assertThat(value).isNotNull(); assertThat(cache.policy().refreshes()).isNotEmpty(); - var future = cache.policy().refreshes().get(context.firstKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.firstKey())); cache.invalidate(context.firstKey()); future.complete(null); @@ -404,6 +405,7 @@ public void refreshIfNeeded_nullFuture(CacheContext context) { @Override public Int load(Int key) { throw new IllegalStateException(); } + @SuppressWarnings("NullAway") @Override public CompletableFuture asyncReload( Int key, Int oldValue, Executor executor) { refreshed.set(true); @@ -463,7 +465,8 @@ public void getIfPresent_delayed(LoadingCache cache, CacheContext cont assertThat(context).removalNotifications().isEmpty(); if (context.isCaffeine()) { - cache.policy().refreshes().get(context.middleKey()).complete(context.middleKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.middleKey())); + future.complete(context.middleKey()); assertThat(context).removalNotifications().withCause(REPLACED) .contains(context.middleKey(), context.original().get(context.middleKey())) .exclusively(); @@ -499,7 +502,7 @@ public void getAllPresent_immediate(LoadingCache cache, CacheContext c assertThat(results).isNotEmpty(); context.ticker().advance(Duration.ofSeconds(45)); assertThat(cache.getAllPresent(context.firstMiddleLastKeys())) - .containsExactlyEntriesIn(Maps.asMap(context.firstMiddleLastKeys(), key -> key)); + .containsExactlyEntriesIn(Maps.toMap(context.firstMiddleLastKeys(), key -> key)); assertThat(cache).hasSize(context.initialSize()); assertThat(context).removalNotifications().withCause(REPLACED) @@ -521,7 +524,8 @@ public void getAllPresent_delayed(LoadingCache cache, CacheContext con if (context.isCaffeine()) { var replaced = new HashMap(); for (var key : context.firstMiddleLastKeys()) { - cache.policy().refreshes().get(key).complete(key); + var future = requireNonNull(cache.policy().refreshes().get(key)); + future.complete(key); replaced.put(key, context.original().get(key)); } assertThat(context).removalNotifications().withCause(REPLACED) @@ -554,7 +558,7 @@ public void getFunc_immediate(LoadingCache cache, CacheContext context @CacheSpec(refreshAfterWrite = Expire.ONE_MINUTE, loader = Loader.ASYNC_INCOMPLETE, population = { Population.PARTIAL, Population.FULL }) public void getFunc_delayed(LoadingCache cache, CacheContext context) { - Function mappingFunction = context.original()::get; + Function mappingFunction = key -> requireNonNull(context.original().get(key)); context.ticker().advance(Duration.ofSeconds(30)); var value = cache.get(context.firstKey(), mappingFunction); assertThat(value).isNotNull(); @@ -563,7 +567,8 @@ public void getFunc_delayed(LoadingCache cache, CacheContext context) assertThat(cache.get(context.lastKey(), mappingFunction)).isEqualTo(context.lastKey().negate()); if (context.isCaffeine()) { - cache.policy().refreshes().get(context.lastKey()).complete(context.lastKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.lastKey())); + future.complete(context.lastKey()); assertThat(context).removalNotifications().withCause(REPLACED) .contains(context.lastKey(), context.original().get(context.lastKey())) .exclusively(); @@ -575,7 +580,7 @@ public void getFunc_delayed(LoadingCache cache, CacheContext context) @CacheSpec(refreshAfterWrite = Expire.ONE_MINUTE, population = { Population.PARTIAL, Population.FULL }) public void getFunc_async(AsyncLoadingCache cache, CacheContext context) { - Function mappingFunction = context.original()::get; + Function mappingFunction = key -> requireNonNull(context.original().get(key)); context.ticker().advance(Duration.ofSeconds(30)); cache.get(context.firstKey(), mappingFunction).join(); context.ticker().advance(Duration.ofSeconds(45)); @@ -620,7 +625,8 @@ public void get_delayed(LoadingCache cache, CacheContext context) { assertThat(cache.get(context.firstKey())).isEqualTo(context.firstKey().negate()); if (context.isCaffeine()) { - cache.policy().refreshes().get(context.firstKey()).complete(context.firstKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.firstKey())); + future.complete(context.firstKey()); assertThat(context).removalNotifications().withCause(REPLACED) .contains(context.firstKey(), context.original().get(context.firstKey())) .exclusively(); @@ -751,7 +757,8 @@ public void getAll_delayed(LoadingCache cache, CacheContext context) { if (context.isCaffeine()) { for (var key : keys) { - cache.policy().refreshes().get(key).complete(key); + var future = requireNonNull(cache.policy().refreshes().get(key)); + future.complete(key); } assertThat(context).removalNotifications().withCause(REPLACED) .contains(Maps.filterKeys(context.original(), context.firstMiddleLastKeys()::contains)) @@ -915,6 +922,7 @@ public void refreshes(LoadingCache cache, CacheContext context) { var future = cache.policy().refreshes().get(context.firstKey()); assertThat(future).isNotNull(); + requireNonNull(future); future.complete(Int.MAX_VALUE); assertThat(cache.policy().refreshes()).isExhaustivelyEmpty(); @@ -929,7 +937,7 @@ public void refreshes_nullLookup(LoadingCache cache, CacheContext cont var value = cache.getIfPresent(context.firstKey()); assertThat(value).isNotNull(); - var future = cache.policy().refreshes().get(context.firstKey()); + var future = requireNonNull(cache.policy().refreshes().get(context.firstKey())); assertThat(cache.policy().refreshes().get(null)).isNull(); assertThat(cache.policy().refreshes().containsKey(null)).isFalse(); assertThat(cache.policy().refreshes().containsValue(null)).isFalse(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReserializableSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReserializableSubject.java index 533d351ef9..46de128f79 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReserializableSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ReserializableSubject.java @@ -17,6 +17,9 @@ import static com.github.benmanes.caffeine.cache.testing.AsyncCacheSubject.asyncCache; import static com.github.benmanes.caffeine.cache.testing.CacheSubject.cache; +import static java.util.Objects.requireNonNull; + +import org.jspecify.annotations.Nullable; import com.github.benmanes.caffeine.cache.Async.AsyncEvictionListener; import com.github.benmanes.caffeine.cache.Async.AsyncExpiry; @@ -39,9 +42,9 @@ public final class ReserializableSubject extends Subject { private final Object actual; - private ReserializableSubject(FailureMetadata metadata, Object subject) { + private ReserializableSubject(FailureMetadata metadata, @Nullable Object subject) { super(metadata, subject); - this.actual = subject; + this.actual = requireNonNull(subject); } public static Factory> asyncReserializable() { @@ -257,7 +260,8 @@ private void checkUnboundedLocalCache( UnboundedLocalCache original, UnboundedLocalCache copy) { check("isRecordingStats").that(copy.isRecordingStats).isEqualTo(original.isRecordingStats); - if (original.removalListener == null) { + if ((original.removalListener == null) || (copy.removalListener == null)) { + check("removalListener").that(original.removalListener).isNull(); check("removalListener").that(copy.removalListener).isNull(); } else if (copy.removalListener.getClass() != original.removalListener.getClass()) { check("removalListener").that(copy.removalListener).isNotNull(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/SchedulerTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/SchedulerTest.java index 5864fd7cc3..8225640a88 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/SchedulerTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/SchedulerTest.java @@ -35,9 +35,11 @@ import java.util.Iterator; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -104,27 +106,28 @@ public void scheduler(Scheduler scheduler) { public void disabledScheduler() { var future = Scheduler.disabledScheduler() .schedule(Runnable::run, () -> {}, 1, TimeUnit.MINUTES); - assertThat(future).isSameInstanceAs(DisabledFuture.INSTANCE); + assertThat(future).isSameInstanceAs(DisabledFuture.instance()); } @Test - public void disabledFuture() { - assertThat(DisabledFuture.INSTANCE.get(0, TimeUnit.SECONDS)).isNull(); - assertThat(DisabledFuture.INSTANCE.isCancelled()).isFalse(); - assertThat(DisabledFuture.INSTANCE.cancel(false)).isFalse(); - assertThat(DisabledFuture.INSTANCE.cancel(true)).isFalse(); - assertThat(DisabledFuture.INSTANCE.isDone()).isTrue(); - assertThat(DisabledFuture.INSTANCE.get()).isNull(); + public void disabledFuture() throws InterruptedException, ExecutionException, TimeoutException { + assertThat(DisabledFuture.instance().get(0, TimeUnit.SECONDS)).isNull(); + assertThat(DisabledFuture.instance().isCancelled()).isFalse(); + assertThat(DisabledFuture.instance().cancel(false)).isFalse(); + assertThat(DisabledFuture.instance().cancel(true)).isFalse(); + assertThat(DisabledFuture.instance().isDone()).isTrue(); + assertThat(DisabledFuture.instance().get()).isNull(); } @Test public void disabledFuture_null() { - npeTester.testAllPublicInstanceMethods(DisabledFuture.INSTANCE); + npeTester.testAllPublicInstanceMethods(DisabledFuture.instance()); } /* --------------- guarded --------------- */ @Test + @SuppressWarnings("NullAway") public void guardedScheduler_null() { assertThrows(NullPointerException.class, () -> Scheduler.guardedScheduler(null)); } @@ -140,7 +143,7 @@ public void guardedScheduler_nullFuture() { var future = Scheduler.guardedScheduler(scheduler) .schedule(executor, command, 1L, TimeUnit.MINUTES); verify(scheduledExecutor).schedule(any(Runnable.class), eq(1L), eq(TimeUnit.MINUTES)); - assertThat(future).isSameInstanceAs(DisabledFuture.INSTANCE); + assertThat(future).isSameInstanceAs(DisabledFuture.instance()); } @Test @@ -154,7 +157,7 @@ public void guardedScheduler() { public void guardedScheduler_exception() { var future = Scheduler.guardedScheduler((r, e, d, u) -> { throw new IllegalStateException(); }) .schedule(Runnable::run, () -> {}, 1, TimeUnit.MINUTES); - assertThat(future).isSameInstanceAs(DisabledFuture.INSTANCE); + assertThat(future).isSameInstanceAs(DisabledFuture.instance()); assertThat(logEvents() .withMessage("Exception thrown by scheduler; discarded task") .withThrowable(IllegalStateException.class) @@ -166,6 +169,7 @@ public void guardedScheduler_exception() { /* --------------- ScheduledExecutorService --------------- */ @Test + @SuppressWarnings("NullAway") public void scheduledExecutorService_null() { assertThrows(NullPointerException.class, () -> Scheduler.forScheduledExecutorService(null)); } @@ -180,7 +184,7 @@ public void scheduledExecutorService_schedule() { var scheduler = Scheduler.forScheduledExecutorService(scheduledExecutor); var future = scheduler.schedule(executor, command, 1L, TimeUnit.MINUTES); - assertThat(future).isNotSameInstanceAs(DisabledFuture.INSTANCE); + assertThat(future).isNotSameInstanceAs(DisabledFuture.instance()); verify(scheduledExecutor).isShutdown(); verify(scheduledExecutor).schedule(task.capture(), eq(1L), eq(TimeUnit.MINUTES)); @@ -200,7 +204,7 @@ public void scheduledExecutorService_shutdown() { when(scheduledExecutor.isShutdown()).thenReturn(true); var scheduler = Scheduler.forScheduledExecutorService(scheduledExecutor); var future = scheduler.schedule(executor, () -> {}, 1L, TimeUnit.MINUTES); - assertThat(future).isSameInstanceAs(DisabledFuture.INSTANCE); + assertThat(future).isSameInstanceAs(DisabledFuture.instance()); verify(scheduledExecutor).isShutdown(); verifyNoMoreInteractions(scheduledExecutor); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/Stresser.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/Stresser.java index f617d5d2ca..cb98552170 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/Stresser.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/Stresser.java @@ -138,7 +138,7 @@ private void status() { System.out.printf(US, "Size = %,d (max: %,d)%n", local.data.mappingCount(), workload.maxEntries); System.out.printf(US, "Lock = [%s%n", StringUtils.substringAfter(evictionLock.toString(), "[")); - System.out.printf(US, "Pending reloads = %,d%n", local.refreshes.size()); + System.out.printf(US, "Pending reloads = %,d%n", local.refreshes().size()); System.out.printf(US, "Pending tasks = %,d%n", ForkJoinPool.commonPool().getQueuedSubmissionCount()); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/StripedBufferTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/StripedBufferTest.java index f345f9f82f..a441ffd4b4 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/StripedBufferTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/StripedBufferTest.java @@ -82,6 +82,7 @@ public void produce(FakeBuffer buffer) { Thread.yield(); } }); + assertThat(buffer.table).isNotNull(); assertThat(buffer.table.length).isAtMost(MAXIMUM_TABLE_SIZE); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java index 4141be2b87..76342abddd 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.LENIENT; import java.lang.ref.ReferenceQueue; import java.time.Duration; @@ -44,15 +45,18 @@ import java.util.stream.IntStream; import java.util.stream.LongStream; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.testng.MockitoSettings; +import org.mockito.testng.MockitoTestNGListener; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.TimerWheel.Sentinel; @@ -65,6 +69,8 @@ * @author ben.manes@gmail.com (Ben Manes) */ @Test(singleThreaded = true) +@MockitoSettings(strictness = LENIENT) +@Listeners(MockitoTestNGListener.class) @SuppressWarnings({"ClassEscapesDefinedScope", "GuardedBy"}) public final class TimerWheelTest { private static final Random random = new Random(); @@ -76,9 +82,8 @@ public final class TimerWheelTest { TimerWheel timerWheel; @BeforeMethod - public void beforeMethod() throws Exception { + public void beforeMethod() { Reset.setThreadLocalRandom(random.nextInt(), random.nextInt()); - MockitoAnnotations.openMocks(this).close(); timerWheel = new TimerWheel<>(); } @@ -203,17 +208,13 @@ public void advance_exception() { @Test(dataProvider = "clock") public void getExpirationDelay_empty(long clock) { - when(cache.evictEntry(any(), any(), anyLong())).thenReturn(true); timerWheel.nanos = clock; - assertThat(timerWheel.getExpirationDelay()).isEqualTo(Long.MAX_VALUE); } @Test(dataProvider = "clock") public void getExpirationDelay_firstWheel(long clock) { - when(cache.evictEntry(any(), any(), anyLong())).thenReturn(true); timerWheel.nanos = clock; - long delay = Duration.ofSeconds(1).toNanos(); timerWheel.schedule(new Timer(clock + delay)); assertThat(timerWheel.getExpirationDelay()).isAtMost(SPANS[0]); @@ -221,9 +222,7 @@ public void getExpirationDelay_firstWheel(long clock) { @Test(dataProvider = "clock") public void getExpirationDelay_lastWheel(long clock) { - when(cache.evictEntry(any(), any(), anyLong())).thenReturn(true); timerWheel.nanos = clock; - long delay = Duration.ofDays(14).toNanos(); timerWheel.schedule(new Timer(clock + delay)); assertThat(timerWheel.getExpirationDelay()).isAtMost(delay); @@ -586,9 +585,10 @@ private void printTimerWheel() { System.err.printf(US, "%nCurrent state:%n%s%n%n", builder.deleteCharAt(builder.length() - 1)); } + @NullUnmarked private static final class Timer extends Node { - Node prev; - Node next; + @Nullable Node prev; + @Nullable Node next; long variableTime; Timer(long accessTime) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue193Test.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue193Test.java index 0c2a143ade..825a53b85a 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue193Test.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue193Test.java @@ -15,12 +15,14 @@ import static com.github.benmanes.caffeine.cache.testing.AsyncCacheSubject.assertThat; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import java.time.Duration; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; +import org.jspecify.annotations.Nullable; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.AsyncCacheLoader; @@ -46,7 +48,7 @@ public final class Issue193Test { private final AtomicLong counter = new AtomicLong(0); private final FakeTicker ticker = new FakeTicker(); - private ListenableFutureTask loadingTask; + private @Nullable ListenableFutureTask loadingTask; private final AsyncCacheLoader loader = (key, executor) -> { // Fools the cache into thinking there is a future that's not immediately ready. @@ -54,7 +56,7 @@ public final class Issue193Test { loadingTask = ListenableFutureTask.create(counter::getAndIncrement); var f = new CompletableFuture(); loadingTask.addListener(() -> { - f.complete(Futures.getUnchecked(loadingTask)); + f.complete(Futures.getUnchecked(requireNonNull(loadingTask))); }, executor); return f; }; @@ -63,6 +65,7 @@ public final class Issue193Test { /** This ensures that any outstanding async loading is completed as well */ private long loadGet(AsyncLoadingCache cache, String key) { CompletableFuture future = cache.get(key); + requireNonNull(loadingTask); if (!loadingTask.isDone()) { loadingTask.run(); } @@ -73,7 +76,8 @@ private long loadGet(AsyncLoadingCache cache, String key) { public void invalidateDuringRefreshRemovalCheck() { var removed = new ArrayList(); AsyncLoadingCache cache = Caffeine.newBuilder() - .removalListener((String key, Long value, RemovalCause reason) -> removed.add(value)) + .removalListener( + (@Nullable String key, @Nullable Long value, RemovalCause reason) -> removed.add(value)) .refreshAfterWrite(Duration.ofNanos(10)) .executor(Runnable::run) .ticker(ticker::read) @@ -87,7 +91,7 @@ public void invalidateDuringRefreshRemovalCheck() { cache.synchronous().invalidate(testKey); // Invalidate key entirely assertThat(cache).doesNotContainKey(testKey); // No value in cache (good) - loadingTask.run(); // Completes refresh + requireNonNull(loadingTask).run(); // Completes refresh assertThat(cache).doesNotContainKey(testKey); // Value in cache (bad) assertThat(removed).containsExactly(0L, 1L).inOrder(); // 1L was sent to removalListener anyway diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Solr10141Test.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Solr10141Test.java index 88279b338b..e3514156ac 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Solr10141Test.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Solr10141Test.java @@ -66,6 +66,7 @@ public void eviction() { var removals = new AtomicLong(); RemovalListener listener = (k, v, removalCause) -> { + assertThat(v).isNotNull(); assertThat(v.key).isEqualTo(k); if (!v.live.compareAndSet(/* expectedValue= */ true, /* newValue= */ false)) { throw new RuntimeException(String.format(US, @@ -143,6 +144,7 @@ public void clear() { var failed = new ConcurrentLinkedQueue(); RemovalListener listener = (k, v, removalCause) -> { + assertThat(v).isNotNull(); assertThat(v.key).isEqualTo(k); if (!v.live.compareAndSet(/* expectedValue= */ true, /* newValue= */ false)) { throw new RuntimeException(String.format(US, diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/AsyncCacheSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/AsyncCacheSubject.java index 02d56c2bb0..d2adb0dd9b 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/AsyncCacheSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/AsyncCacheSubject.java @@ -20,10 +20,13 @@ import static com.github.benmanes.caffeine.cache.testing.CacheSubject.cache; import static com.github.benmanes.caffeine.testing.MapSubject.map; import static com.google.common.truth.Truth.assertAbout; +import static java.util.Objects.requireNonNull; import java.util.Map; import java.util.concurrent.Future; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.AsyncCache; import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; @@ -36,9 +39,9 @@ public final class AsyncCacheSubject extends Subject { private final AsyncCache actual; - private AsyncCacheSubject(FailureMetadata metadata, AsyncCache subject) { + private AsyncCacheSubject(FailureMetadata metadata, @Nullable AsyncCache subject) { super(metadata, subject); - this.actual = subject; + this.actual = requireNonNull(subject); } public static Factory> asyncCache() { @@ -81,7 +84,7 @@ public void doesNotContainKey(Object key) { } /** Fails if the cache does not contain the given value. */ - public void containsValue(Object value) { + public void containsValue(@Nullable Object value) { if (value instanceof Future) { check("cache").about(map()).that(actual.asMap()).containsValue(value); } else { @@ -90,7 +93,7 @@ public void containsValue(Object value) { } /** Fails if the cache does not contain the given entry. */ - public void containsEntry(Object key, Object value) { + public void containsEntry(Object key, @Nullable Object value) { if (value instanceof Future) { check("cache").that(actual.asMap()).containsEntry(key, value); } else { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContext.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContext.java index 016c317c0b..26a0d8d8eb 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContext.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContext.java @@ -30,6 +30,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.function.Function; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import com.github.benmanes.caffeine.cache.AsyncCache; @@ -92,7 +93,6 @@ public final class CacheContext { final TrackingExecutor executor; final ReferenceType keyStrength; final CacheWeigher cacheWeigher; - final Expiry expiry; final Map original; final CacheExpiry expiryType; final Population population; @@ -106,6 +106,8 @@ public final class CacheContext { final Loader loader; final Stats stats; + final @Nullable Expiry expiry; + final boolean isAsyncLoader; CacheBuilder guava; @@ -124,7 +126,7 @@ public final class CacheContext { @Nullable Map absent; - @SuppressWarnings({"PMD.ExcessiveParameterList", "TooManyParameters"}) + @SuppressWarnings({"NullAway.Init", "PMD.ExcessiveParameterList", "TooManyParameters"}) public CacheContext(InitialCapacity initialCapacity, Stats stats, CacheWeigher cacheWeigher, Maximum maximumSize, CacheExpiry expiryType, Expire afterAccess, Expire afterWrite, Expire refresh, ReferenceType keyStrength, ReferenceType valueStrength, @@ -159,7 +161,7 @@ public CacheContext(InitialCapacity initialCapacity, Stats stats, CacheWeigher c this.compute = compute; this.expiryType = expiryType; this.expiryTime = cacheSpec.expiryTime(); - this.expiry = expiryType.createExpiry(expiryTime); + this.expiry = (expiryType == CacheExpiry.DISABLED) ? null : expiryType.createExpiry(expiryTime); } /** Returns a thread local interner for explicit caching. */ @@ -214,16 +216,19 @@ public Population population() { public Int firstKey() { assertWithMessage("Invalid usage of context").that(firstKey).isNotNull(); + requireNonNull(firstKey); return firstKey; } public Int middleKey() { assertWithMessage("Invalid usage of context").that(middleKey).isNotNull(); + requireNonNull(middleKey); return middleKey; } public Int lastKey() { assertWithMessage("Invalid usage of context").that(lastKey).isNotNull(); + requireNonNull(lastKey); return lastKey; } @@ -417,6 +422,7 @@ public boolean expiresVariably() { return (expiryType != CacheExpiry.DISABLED); } + @NullUnmarked public Expiry expiry() { return expiry; } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContextSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContextSubject.java index a61b262db9..4938f9dc0d 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContextSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheContextSubject.java @@ -39,6 +39,8 @@ import java.util.function.Function; import java.util.function.ToLongFunction; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Policy.CacheEntry; import com.github.benmanes.caffeine.cache.RemovalCause; @@ -65,9 +67,9 @@ public final class CacheContextSubject extends Subject { private final CacheContext actual; - CacheContextSubject(FailureMetadata metadata, CacheContext subject) { + CacheContextSubject(FailureMetadata metadata, @Nullable CacheContext subject) { super(metadata, subject); - this.actual = subject; + this.actual = requireNonNull(subject); } public static Factory context() { @@ -212,9 +214,9 @@ public static final class StatsSubject extends Subject { private final CacheContext actual; private final boolean isDirect; - private StatsSubject(FailureMetadata metadata, CacheContext context) { + private StatsSubject(FailureMetadata metadata, @Nullable CacheContext context) { super(metadata, context); - this.actual = context; + this.actual = requireNonNull(context); this.isDirect = !context.isRecordingStats() || (context.executorType() == CacheExecutor.DIRECT); } @@ -290,6 +292,7 @@ private ListenerSubject(FailureMetadata metadata, CacheContext context, private static Factory factoryOf( RemovalListenerType... removalListenerTypes) { return (metadata, context) -> { + requireNonNull(context); var subject = Arrays.stream(removalListenerTypes) .filter(listener -> listener.isConsumingListener(context)) .collect(toImmutableMap(identity(), listener -> listener.instance(context))); @@ -356,7 +359,7 @@ private WithCause(RemovalCause cause) { } @CanIgnoreReturnValue - public Exclusive contains(Int key, Int value) { + public Exclusive contains(@Nullable Int key, @Nullable Int value) { return contains(new SimpleEntry<>(key, value)); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheProvider.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheProvider.java index ba281323ef..aa7d45cfef 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheProvider.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheProvider.java @@ -100,7 +100,7 @@ private Object[] asTestCases(CacheContext context) { } params[i] = context.cache().asMap(); } else if (clazz.isAssignableFrom(Policy.Eviction.class)) { - params[i] = context.cache().policy().eviction().orElse(null); + params[i] = context.cache().policy().eviction().orElseThrow(); } else if (clazz.isAssignableFrom(Policy.VarExpiration.class)) { params[i] = context.cache().policy().expireVariably().orElseThrow(); } else if (clazz.isAssignableFrom(Policy.FixedRefresh.class)) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSpec.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSpec.java index 69f6cfa86c..d1d5c7e96d 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSpec.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSpec.java @@ -54,6 +54,7 @@ import com.github.benmanes.caffeine.cache.Scheduler; import com.github.benmanes.caffeine.cache.Weigher; import com.github.benmanes.caffeine.cache.testing.RemovalListeners.ConsumingRemovalListener; +import com.github.benmanes.caffeine.cache.testing.Weighers.SkippedWeigher; import com.github.benmanes.caffeine.testing.ConcurrentTestHarness; import com.github.benmanes.caffeine.testing.Int; import com.google.common.collect.Maps; @@ -198,12 +199,18 @@ enum CacheWeigher { VALUE(() -> (key, value) -> Math.abs(((Int) value).intValue())), /** A flag indicating that the entry is weighted by the value's collection size. */ COLLECTION(() -> (key, value) -> ((Collection) value).size()), - /** A flag indicating that the entry's weight is randomly changing. */ + /** + * A flag indicating that the entry's weight is randomly changing and is skipped by automatic + * validation checks. + */ RANDOM(Weighers::random), - /** A flag indicating that the entry's weight records interactions. */ + /** + * A flag indicating that the entry's weight records interactions and is skipped by automatic + * automatic validation checks. + */ @SuppressWarnings("unchecked") MOCKITO(() -> { - Weigher weigher = Mockito.mock(); + SkippedWeigher weigher = Mockito.mock(); when(weigher.weigh(any(), any())).thenReturn(1); return weigher; }); @@ -267,7 +274,7 @@ CacheExpiry[] expiry() default { enum CacheExpiry { DISABLED { @Override public Expiry createExpiry(Expire expiryTime) { - return null; + throw new AssertionError(); } }, MOCKITO { @@ -419,6 +426,7 @@ enum Loader implements CacheLoader { }, /** A loader that always returns null (no mapping). */ NULL { + @SuppressWarnings("NullAway") @Override public Int load(Int key) { return null; } @@ -461,7 +469,9 @@ enum Loader implements CacheLoader { @Override public Int load(Int key) { throw new UnsupportedOperationException(); } - @SuppressWarnings({"PMD.ReturnEmptyCollectionRatherThanNull", "ReturnsNullCollection"}) + + @SuppressWarnings({"NullAway", + "PMD.ReturnEmptyCollectionRatherThanNull", "ReturnsNullCollection"}) @Override public Map loadAll(Set keys) { return null; } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSubject.java index 726e299940..c37db5ff51 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSubject.java @@ -21,10 +21,13 @@ import static com.github.benmanes.caffeine.testing.MapSubject.map; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.truth.Truth.assertAbout; +import static java.util.Objects.requireNonNull; import java.util.Map; import java.util.Objects; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.Cache; import com.google.common.testing.GcFinalization; import com.google.common.truth.Correspondence; @@ -44,9 +47,9 @@ public final class CacheSubject extends Subject { private final Cache actual; - CacheSubject(FailureMetadata metadata, Cache subject) { + CacheSubject(FailureMetadata metadata, @Nullable Cache subject) { super(metadata, subject); - this.actual = subject; + this.actual = requireNonNull(subject); } public static Factory> cache() { @@ -104,7 +107,7 @@ public void doesNotContainKey(Object key) { } /** Fails if the cache does not contain the given value. */ - public void containsValue(Object value) { + public void containsValue(@Nullable Object value) { check("cache").about(map()).that(actual.asMap()).containsValue(value); } @@ -114,14 +117,16 @@ public void doesNotContainValue(Object value) { } /** Fails if the cache does not contain the given entry. */ - public void containsEntry(Object key, Object value) { + public void containsEntry(Object key, @Nullable Object value) { + requireNonNull(value); check("cache").that(actual.asMap()) .comparingValuesUsing(EQUALITY) .containsEntry(key, value); } /** Fails if the cache contains the given entry. */ - public void doesNotContainEntry(Object key, Object value) { + public void doesNotContainEntry(Object key, @Nullable Object value) { + requireNonNull(value); check("cache").that(actual.asMap()) .comparingValuesUsing(EQUALITY) .doesNotContainEntry(key, value); @@ -162,8 +167,8 @@ public static final class CleanUpSubject extends Subject { private final Cache actual; - private CleanUpSubject(FailureMetadata metadata, Cache cache) { - super(metadata, cache.asMap()); + private CleanUpSubject(FailureMetadata metadata, @Nullable Cache cache) { + super(metadata, requireNonNull(cache).asMap()); this.actual = cache; } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/GuavaCacheFromContext.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/GuavaCacheFromContext.java index 712338ba5e..1a6af68282 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/GuavaCacheFromContext.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/GuavaCacheFromContext.java @@ -34,6 +34,8 @@ import java.util.function.BiFunction; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.Policy; @@ -138,7 +140,8 @@ public static Cache newGuavaCache(CacheContext context) { return castedCache; } - static class GuavaCache implements Cache, Serializable { + @SuppressWarnings("NullAway") + static class GuavaCache implements Cache, Serializable { private static final long serialVersionUID = 1L; private final com.google.common.cache.Cache cache; @@ -146,10 +149,10 @@ static class GuavaCache implements Cache, Serializable { private final boolean canSnapshot; private final Ticker ticker; - transient ConcurrentMap mapView; transient StatsCounter statsCounter; - transient Policy policy; - transient Set keySet; + transient @Nullable ConcurrentMap mapView; + transient @Nullable Policy policy; + transient @Nullable Set keySet; GuavaCache(com.google.common.cache.Cache cache, CacheContext context) { this.canSnapshot = context.expires() || context.refreshes(); @@ -160,12 +163,12 @@ static class GuavaCache implements Cache, Serializable { } @Override - public V getIfPresent(Object key) { + public @Nullable V getIfPresent(Object key) { return cache.getIfPresent(key); } @Override - public V get(K key, Function mappingFunction) { + public @Nullable V get(K key, Function mappingFunction) { requireNonNull(mappingFunction); try { return cache.get(key, () -> { @@ -201,7 +204,7 @@ public Map getAll(Iterable keys, Function found = getAllPresent(keys); + Map found = getAllPresent(keys); Set keysToLoad = Sets.difference(ImmutableSet.copyOf(keys), found.keySet()); if (keysToLoad.isEmpty()) { return found; @@ -494,6 +497,7 @@ static class GuavaLoadingCache extends GuavaCache implements Loading } @Override + @SuppressWarnings("NullAway") public V get(K key) { try { return cache.get(key); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java index 18a517e025..4f98c8b5a2 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java @@ -22,6 +22,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.RejectedExecutionException; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; @@ -44,7 +46,7 @@ public static RemovalListener rejecting() { return new RejectingRemovalListener<>(); } - private static void validate(Object key, Object value, RemovalCause cause) { + private static void validate(@Nullable Object key, @Nullable Object value, RemovalCause cause) { if (cause != RemovalCause.COLLECTED) { requireNonNull(key); requireNonNull(value); @@ -60,7 +62,7 @@ public static final class RejectingRemovalListener public int rejected; @Override - public void onRemoval(K key, V value, RemovalCause cause) { + public void onRemoval(@Nullable K key, @Nullable V value, RemovalCause cause) { validate(key, value, cause); if (reject) { @@ -83,7 +85,7 @@ public ConsumingRemovalListener() { } @Override - public void onRemoval(K key, V value, RemovalCause cause) { + public void onRemoval(@Nullable K key, @Nullable V value, RemovalCause cause) { validate(key, value, cause); removed.add(new RemovalNotification<>(key, value, cause)); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/Weighers.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/Weighers.java index d1897c0354..b690593398 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/Weighers.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/Weighers.java @@ -42,6 +42,9 @@ public static Weigher random() { return (Weigher) RandomWeigher.INSTANCE; } + /** A marker to instruct the validation to not check the entry's weight for data consistency. */ + public interface SkippedWeigher extends Weigher {} + static final class ConstantWeigher implements Weigher, Serializable { private static final long serialVersionUID = 1L; @@ -57,7 +60,7 @@ static final class ConstantWeigher implements Weigher, Serializable } } - enum RandomWeigher implements Weigher { + enum RandomWeigher implements SkippedWeigher { INSTANCE; @Override public int weigh(Object key, Object value) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/acceptance/ConcurrentHashMapAcceptanceTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/acceptance/ConcurrentHashMapAcceptanceTest.java index e9addaf1a2..780306989a 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/acceptance/ConcurrentHashMapAcceptanceTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/acceptance/ConcurrentHashMapAcceptanceTest.java @@ -25,6 +25,7 @@ import org.eclipse.collections.impl.list.Interval; import org.eclipse.collections.impl.parallel.ParallelIterate; import org.eclipse.collections.impl.test.Verify; +import org.jspecify.annotations.NullUnmarked; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -35,6 +36,7 @@ * * Ported from Eclipse Collections 11.0. */ +@NullUnmarked @SuppressWarnings("CanIgnoreReturnValueSuggester") public abstract class ConcurrentHashMapAcceptanceTest { private ExecutorService executor; diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/mutable/ConcurrentHashMapTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/mutable/ConcurrentHashMapTest.java index c53e059d3f..fc01297a1b 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/mutable/ConcurrentHashMapTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/eclipse/mutable/ConcurrentHashMapTest.java @@ -35,6 +35,7 @@ import org.eclipse.collections.impl.set.mutable.UnifiedSet; import org.eclipse.collections.impl.test.Verify; import org.eclipse.collections.impl.tuple.ImmutableEntry; +import org.jspecify.annotations.NullUnmarked; import org.junit.Assert; import org.junit.Test; @@ -43,6 +44,7 @@ * * Ported from Eclipse Collections 11.0. */ +@NullUnmarked @SuppressWarnings({"all", "CanIgnoreReturnValueSuggester", "IdentityConversion", "unchecked"}) public abstract class ConcurrentHashMapTest extends ConcurrentHashMapTestCase { public static final MutableMap> SMALL_BAG_MUTABLE_MAP = diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/Collection8Test.java b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/Collection8Test.java index 9dbab2ff63..30547dff2a 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/Collection8Test.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/Collection8Test.java @@ -45,12 +45,15 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import org.jspecify.annotations.NullUnmarked; + import junit.framework.Test; /** * Contains tests applicable to all jdk8+ Collection implementations. * An extension of CollectionTest. */ +@NullUnmarked @SuppressWarnings({"CatchAndPrintStackTrace", "CollectionAddAllToCollectionBlock", "CollectionForEach", "CollectionIsEmpty", "CollectionToArray", "CollectorMutability", "EmptyCatch", "LabelledBreakTarget", "MemberName", "MethodReferenceUsage", "MissingDefault", diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/ConcurrentHashMap8Test.java b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/ConcurrentHashMap8Test.java index 84bc3b750e..84ba6ede33 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/ConcurrentHashMap8Test.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/ConcurrentHashMap8Test.java @@ -24,12 +24,15 @@ import java.util.concurrent.atomic.LongAdder; import java.util.function.BiFunction; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import junit.framework.Test; import junit.framework.TestSuite; +@NullUnmarked @SuppressWarnings({"EmptyCatch", "IdentityConversion", "PreferredInterfaceType", "rawtypes", "try", "unchecked", "UnnecessaryFinal"}) public class ConcurrentHashMap8Test extends JSR166TestCase { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/JSR166TestCase.java b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/JSR166TestCase.java index 905872dfaa..a6928cc4a0 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/JSR166TestCase.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/JSR166TestCase.java @@ -99,6 +99,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import junit.framework.Test; @@ -193,6 +194,7 @@ * * */ +@NullUnmarked @SuppressWarnings({"AnnotateFormatMethod", "ClassEscapesDefinedScope", "CollectionToArray", "ConstantField", "EmptyCatch", "EqualsIncompatibleType", "FunctionalInterfaceClash", "InterruptedExceptionSwallowed", "JavaUtilDate", "JUnit3FloatingPointComparisonWithoutDelta", diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/MapTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/MapTest.java index e049ab2d39..7b6b06d7d1 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/MapTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/jsr166/MapTest.java @@ -16,11 +16,14 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiFunction; +import org.jspecify.annotations.NullUnmarked; + import junit.framework.Test; /** * Contains tests applicable to all Map implementations. */ +@NullUnmarked @SuppressWarnings({"rawtypes", "unchecked", "UnnecessaryFinal", "UnnecessaryParentheses"}) public class MapTest extends JSR166TestCase { final MapImplementation impl; diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/lincheck/AbstractLincheckCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/lincheck/AbstractLincheckCacheTest.java index ef523ab511..798dcd5b9f 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/lincheck/AbstractLincheckCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/lincheck/AbstractLincheckCacheTest.java @@ -25,6 +25,7 @@ import org.jetbrains.kotlinx.lincheck.paramgen.IntGen; import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions; import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions; +import org.jspecify.annotations.Nullable; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.Caffeine; @@ -76,7 +77,7 @@ public void stressTest() { /* --------------- Cache --------------- */ @Operation - public Integer getIfPresent(@Param(name = "key") int key) { + public @Nullable Integer getIfPresent(@Param(name = "key") int key) { return cache.getIfPresent(key); } @@ -110,7 +111,7 @@ public boolean containsKey(@Param(name = "key") int key) { } @Operation - public Integer get_asMap(@Param(name = "key") int key) { + public @Nullable Integer get_asMap(@Param(name = "key") int key) { return cache.asMap().get(key); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/CollectionSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/CollectionSubject.java index 8dc625538e..41e3047533 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/CollectionSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/CollectionSubject.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.truth.Truth.assertAbout; +import static java.util.Objects.requireNonNull; import java.util.Collection; import java.util.Deque; @@ -25,6 +26,8 @@ import java.util.Queue; import java.util.Set; +import org.jspecify.annotations.Nullable; + import com.google.common.truth.FailureMetadata; import com.google.common.truth.IterableSubject; @@ -34,9 +37,9 @@ * @author ben.manes@gmail.com (Ben Manes) */ public class CollectionSubject extends IterableSubject { - private final Collection actual; + private final @Nullable Collection actual; - public CollectionSubject(FailureMetadata metadata, Collection subject) { + public CollectionSubject(FailureMetadata metadata, @Nullable Collection subject) { super(metadata, subject); this.actual = subject; } @@ -56,6 +59,7 @@ public final void hasSize(long expectedSize) { /** Fails if the collection does not have less than the given size. */ public void hasSizeLessThan(long other) { + requireNonNull(actual); checkArgument(other >= 0, "expectedSize (%s) must be >= 0", other); check("size()").that(actual.size()).isLessThan(Math.toIntExact(other)); } @@ -83,11 +87,13 @@ public void isExhaustivelyEmpty() { } private void checkIterable() { + requireNonNull(actual); check("iterator().hasNext()").that(actual.iterator().hasNext()).isFalse(); } @SuppressWarnings("CollectionToArray") private void checkCollection() { + requireNonNull(actual); check("size()").that(actual).hasSize(0); check("isEmpty()").that(actual).isEmpty(); check("toArray()").that(actual.toArray()).isEmpty(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/FutureSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/FutureSubject.java index d9272f94ed..0c88752773 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/FutureSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/FutureSubject.java @@ -16,11 +16,14 @@ package com.github.benmanes.caffeine.testing; import static com.google.common.truth.Truth.assertAbout; +import static java.util.Objects.requireNonNull; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import org.jspecify.annotations.Nullable; + import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; import com.google.common.truth.ThrowableSubject; @@ -32,9 +35,9 @@ * @author ben.manes@gmail.com (Ben Manes) */ public final class FutureSubject extends Subject { - private final CompletableFuture actual; + private final @Nullable CompletableFuture actual; - private FutureSubject(FailureMetadata metadata, CompletableFuture subject) { + private FutureSubject(FailureMetadata metadata, @Nullable CompletableFuture subject) { super(metadata, subject); this.actual = subject; } @@ -43,12 +46,13 @@ public static Factory> future() { return FutureSubject::new; } - public static FutureSubject assertThat(CompletableFuture actual) { + public static FutureSubject assertThat(@Nullable CompletableFuture actual) { return assertAbout(future()).that(actual); } /** Fails if the future is not done. */ public void isDone() { + requireNonNull(actual); if (!actual.isDone()) { failWithActual("expected to be done", actual); } @@ -56,6 +60,7 @@ public void isDone() { /** Fails if the future is done. */ public void isNotDone() { + requireNonNull(actual); if (actual.isDone()) { failWithActual("expected to not be done", actual); } @@ -63,6 +68,7 @@ public void isNotDone() { /** Fails if the future is has not completed exceptionally. */ public void hasCompletedExceptionally() { + requireNonNull(actual); if (!actual.isCompletedExceptionally()) { failWithActual("expected to be completed exceptionally", actual.join()); } @@ -70,6 +76,7 @@ public void hasCompletedExceptionally() { /** Fails if the future is not successful with the given value. */ public void succeedsWith(int value) { + requireNonNull(actual); var result = actual.join(); if (result instanceof Int) { check("future").that(result).isEqualTo(Int.valueOf(value)); @@ -79,18 +86,21 @@ public void succeedsWith(int value) { } /** Fails if the future is not successful with the given value. */ - public void succeedsWith(Object value) { + public void succeedsWith(@Nullable Object value) { + requireNonNull(actual); check("future").that(actual.join()).isEqualTo(value); } /** Fails if the future is not successful with a null value. */ public void succeedsWithNull() { + requireNonNull(actual); check("future").that(actual.join()).isNull(); } /** Fails if the future is did not fail with the given join() exception. */ @CanIgnoreReturnValue public ThrowableSubject failsWith(Class clazz) { + requireNonNull(actual); try { failWithActual("join", actual.join()); throw new AssertionError(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/Int.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/Int.java index 89da30ee3d..92ecba261e 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/Int.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/Int.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.testing; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import java.io.Serializable; import java.util.ArrayList; @@ -26,6 +27,8 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; +import org.jspecify.annotations.Nullable; + import com.google.errorprone.annotations.Immutable; /** @@ -48,8 +51,8 @@ public final class Int implements Serializable { private final int value; /** Constructs a newly allocated {@code Int} object with the same {@code value}. */ - public Int(Int value) { - this(value.value); + public Int(@Nullable Int value) { + this(requireNonNull(value).value); } /** diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/IntSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/IntSubject.java index 9d034d545e..d53e9a0f67 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/IntSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/IntSubject.java @@ -17,6 +17,8 @@ import static com.google.common.truth.Truth.assertAbout; +import org.jspecify.annotations.Nullable; + import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; @@ -27,7 +29,7 @@ */ public final class IntSubject extends Subject { - private IntSubject(FailureMetadata metadata, Int subject) { + private IntSubject(FailureMetadata metadata, @Nullable Int subject) { super(metadata, subject); } @@ -35,7 +37,7 @@ public static Factory integer() { return IntSubject::new; } - public static IntSubject assertThat(Int actual) { + public static IntSubject assertThat(@Nullable Int actual) { return assertAbout(integer()).that(actual); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/LoggingEvents.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/LoggingEvents.java index 4b793c3624..cd1b19bcc3 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/LoggingEvents.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/LoggingEvents.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.slf4j.event.Level; import com.github.valfirst.slf4jtest.LoggingEvent; @@ -43,7 +44,8 @@ public final class LoggingEvents extends ForwardingList { private final List> predicates; private final ImmutableList events; - private ImmutableList filteredEvents; + private @Nullable ImmutableList filteredEvents; + private boolean exclusive; private LoggingEvents(Iterable events) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/MapSubject.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/MapSubject.java index 32c767e4da..eae84aced1 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/MapSubject.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/MapSubject.java @@ -18,9 +18,12 @@ import static com.github.benmanes.caffeine.testing.CollectionSubject.collection; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.truth.Truth.assertAbout; +import static java.util.Objects.requireNonNull; import java.util.Map; +import org.jspecify.annotations.Nullable; + import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; import com.google.common.truth.FailureMetadata; @@ -34,18 +37,19 @@ */ public class MapSubject extends com.google.common.truth.MapSubject { @SuppressWarnings("ImmutableMemberCollection") - private final Map actual; + private final @Nullable Map actual; - public MapSubject(FailureMetadata metadata, Map subject) { + public MapSubject(FailureMetadata metadata, @Nullable Map subject) { super(metadata, subject); this.actual = subject; } + @SuppressWarnings("NullAway") public static Factory> map() { return MapSubject::new; } - public static MapSubject assertThat(Map actual) { + public static MapSubject assertThat(@Nullable Map actual) { return assertAbout(map()).that(actual); } @@ -56,29 +60,34 @@ public final void hasSize(long expectedSize) { /** Fails if the map does not have less than the given size. */ public void hasSizeLessThan(long other) { + requireNonNull(actual); checkArgument(other >= 0, "expectedSize (%s) must be >= 0", other); check("size()").that(actual.size()).isLessThan(Math.toIntExact(other)); } /** Fails if the map's size is not in {@code range}. */ public void hasSizeIn(Range range) { + requireNonNull(actual); check("size()").that(actual.size()).isIn(range); } /** Fails if the map does not contain the given keys, where duplicate keys are ignored. */ @CanIgnoreReturnValue public Ordered containsExactlyKeys(Iterable keys) { + requireNonNull(actual); return check("containsKeys").that(actual.keySet()) .containsExactlyElementsIn(ImmutableSet.copyOf(keys)); } /** Fails if the map does not contain the given value. */ - public void containsValue(Object value) { + public void containsValue(@Nullable Object value) { + requireNonNull(actual); check("containsValue").that(actual.values()).contains(value); } /** Fails if the map does contain the given value. */ public void doesNotContainValue(Object value) { + requireNonNull(actual); check("containsValue").that(actual.values()).doesNotContain(value); } @@ -87,6 +96,7 @@ public void doesNotContainValue(Object value) { * methods. */ public void isExhaustivelyEmpty() { + requireNonNull(actual); isEqualTo(Map.of()); hasSize(0); isEmpty(); diff --git a/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml b/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml index 8ccad706c3..85ee0daab6 100644 --- a/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml +++ b/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] caffeine = "3.1.8" junit = "5.11.3" -reactor = "3.7.0" +reactor = "3.7.1" truth = "1.4.4" versions = "0.51.0" diff --git a/examples/coalescing-bulkloader-reactor/gradle/wrapper/gradle-wrapper.properties b/examples/coalescing-bulkloader-reactor/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/examples/coalescing-bulkloader-reactor/gradle/wrapper/gradle-wrapper.properties +++ b/examples/coalescing-bulkloader-reactor/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/examples/coalescing-bulkloader-reactor/settings.gradle.kts b/examples/coalescing-bulkloader-reactor/settings.gradle.kts index 149babf6c3..53023a7268 100644 --- a/examples/coalescing-bulkloader-reactor/settings.gradle.kts +++ b/examples/coalescing-bulkloader-reactor/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/examples/graal-native/gradle/wrapper/gradle-wrapper.properties b/examples/graal-native/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/examples/graal-native/gradle/wrapper/gradle-wrapper.properties +++ b/examples/graal-native/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/examples/graal-native/settings.gradle.kts b/examples/graal-native/settings.gradle.kts index a5e4cd278c..1bb26f570f 100644 --- a/examples/graal-native/settings.gradle.kts +++ b/examples/graal-native/settings.gradle.kts @@ -5,7 +5,7 @@ pluginManagement { } } plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/examples/hibernate/gradle/wrapper/gradle-wrapper.properties b/examples/hibernate/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/examples/hibernate/gradle/wrapper/gradle-wrapper.properties +++ b/examples/hibernate/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/examples/hibernate/settings.gradle.kts b/examples/hibernate/settings.gradle.kts index 8a0dbec0f9..a6fd48777d 100644 --- a/examples/hibernate/settings.gradle.kts +++ b/examples/hibernate/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/examples/indexable/gradle/wrapper/gradle-wrapper.properties b/examples/indexable/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/examples/indexable/gradle/wrapper/gradle-wrapper.properties +++ b/examples/indexable/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/examples/indexable/settings.gradle.kts b/examples/indexable/settings.gradle.kts index e17d97cef9..58d363260e 100644 --- a/examples/indexable/settings.gradle.kts +++ b/examples/indexable/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/examples/resilience-failsafe/gradle/wrapper/gradle-wrapper.properties b/examples/resilience-failsafe/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/examples/resilience-failsafe/gradle/wrapper/gradle-wrapper.properties +++ b/examples/resilience-failsafe/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/examples/resilience-failsafe/settings.gradle.kts b/examples/resilience-failsafe/settings.gradle.kts index 0ddb809517..090e9af4ed 100644 --- a/examples/resilience-failsafe/settings.gradle.kts +++ b/examples/resilience-failsafe/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/examples/write-behind-rxjava/gradle/wrapper/gradle-wrapper.properties b/examples/write-behind-rxjava/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/examples/write-behind-rxjava/gradle/wrapper/gradle-wrapper.properties +++ b/examples/write-behind-rxjava/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/examples/write-behind-rxjava/settings.gradle.kts b/examples/write-behind-rxjava/settings.gradle.kts index d04c4ad195..c288943379 100644 --- a/examples/write-behind-rxjava/settings.gradle.kts +++ b/examples/write-behind-rxjava/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fa913b441d..22aeddf9aa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,14 +7,14 @@ bnd = "7.1.0" bouncycastle-jdk18on = "1.79" cache2k = "2.6.1.Final" caffeine = "3.1.8" -checkstyle = "10.20.2" +checkstyle = "10.21.0" coherence = "24.09" commons-collections4 = "4.4" commons-compress = "1.27.1" commons-io = "2.18.0" commons-lang3 = "3.17.0" commons-math3 = "3.6.1" -commons-text = "1.12.0" +commons-text = "1.13.0" concurrentlinkedhashmap = "1.4.2" config = "1.4.3" coveralls = "2.12.2" @@ -32,7 +32,7 @@ felix-scr = "2.2.12" findsecbugs = "1.13.0" flip-tables = "1.1.1" forbidden-apis = "3.8" -google-java-format = "1.25.0" +google-java-format = "1.25.2" guava = "33.3.1-jre" guice = "6.0.0" h2 = "2.3.232" @@ -67,8 +67,9 @@ jvm-dependency-conflict-resolution = "2.1.2" kotlin = "2.1.0" lincheck = "2.34" mockito = "5.14.2" +mockito-testng = "0.5.2" nexus-publish = "2.0.0" -nullaway = "0.12.1" +nullaway = "0.12.2" nullaway-plugin = "2.1.0" okhttp-bom = "4.12.0" okio-bom = "3.9.1" @@ -85,7 +86,7 @@ slf4j-test = "3.0.1" snakeyaml = "2.3" sonarqube = "6.0.1.5171" spotbugs = "4.8.6" -spotbugs-contrib = "7.6.8" +spotbugs-contrib = "7.6.9" spotbugs-plugin = "6.0.26" stream = "2.9.8" tcache = "2.0.1" @@ -171,7 +172,9 @@ junit5-vintage = { module = "org.junit.vintage:junit-vintage-engine", version.re kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" } lincheck = { module = "org.jetbrains.kotlinx:lincheck-jvm", version.ref = "lincheck" } mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } +mockito-testng = { module = "org.mockito:mockito-testng", version.ref = "mockito-testng" } nullaway = { module = "com.uber.nullaway:nullaway", version.ref = "nullaway" } +nullaway-annotations = { module = "com.uber.nullaway:nullaway-annotations", version.ref = "nullaway" } okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp-bom" } okio-bom = { module = "com.squareup.okio:okio-bom", version.ref = "okio-bom" } osgi-annotations = { module = "org.osgi:org.osgi.service.component.annotations", version.ref = "osgi-annotations" } @@ -211,6 +214,7 @@ constraints = ["bcel", "bouncycastle-jdk18on", "commons-compress", "commons-text errorprone-support = ["errorprone-support", "errorprone-support-refaster"] junit = ["junit4", "junit5"] junit-engines = ["junit5-vintage", "junit5-testng"] +mockito = ["mockito", "mockito-testng"] osgi-test-compile = ["pax-exam-junit4"] osgi-test-runtime = ["felix-framework", "felix-scr", "osgi-function", "osgi-promise", "pax-exam-container-native", "pax-exam-link-mvn", "pax-url-aether"] diff --git a/gradle/plugins/settings.gradle.kts b/gradle/plugins/settings.gradle.kts index 613c7e28ec..c8e64b1f28 100644 --- a/gradle/plugins/settings.gradle.kts +++ b/gradle/plugins/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } diff --git a/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts index eeb0e7902a..440f9c230e 100644 --- a/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/lifecycle/java-library.caffeine.gradle.kts @@ -41,7 +41,7 @@ tasks.withType().configureEach { javaModuleVersion = provider { version as String } compilerArgs.addAll(listOf("-Xlint:all", "-Xlint:-auxiliaryclass", "-Xlint:-classfile", "-Xlint:-exports", "-Xlint:-processing", "-Xlint:-removal", "-Xlint:-requires-automatic", - "-parameters")) + "-parameters", "-Xmaxerrs", "500", "-Xmaxwarns", "500")) if (isCI()) { compilerArgs.add("-Werror") } diff --git a/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts index 56f34e130a..d50e839965 100644 --- a/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts @@ -1,9 +1,12 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED +import net.ltgt.gradle.errorprone.errorprone +import net.ltgt.gradle.nullaway.nullaway plugins { `java-library` + id("errorprone.caffeine") } val mockitoAgent: Configuration by configurations.creating @@ -13,11 +16,12 @@ dependencies { testImplementation(libs.guice) testImplementation(libs.truth) testImplementation(libs.testng) - testImplementation(libs.mockito) testImplementation(libs.hamcrest) testImplementation(libs.awaitility) testImplementation(libs.bundles.junit) testImplementation(libs.guava.testlib) + testImplementation(libs.bundles.mockito) + testImplementation(libs.nullaway.annotations) testImplementation(libs.bundles.osgi.test.compile) testImplementation(platform(libs.asm.bom)) @@ -52,3 +56,18 @@ tasks.withType().configureEach { showCauses = true } } + +tasks.named("compileTestJava").configure { + options.errorprone.nullaway { + customInitializerAnnotations.addAll(listOf( + "org.testng.annotations.BeforeClass", + "org.testng.annotations.BeforeMethod")) + externalInitAnnotations.addAll(listOf( + "org.mockito.testng.MockitoSettings", + "picocli.CommandLine.Command")) + excludedFieldAnnotations.addAll(listOf( + "jakarta.inject.Inject", + "org.mockito.Captor", + "org.mockito.Mock")) + } +} diff --git a/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts index 115c71bedc..6be2731475 100644 --- a/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts @@ -61,12 +61,8 @@ tasks.withType().configureEach { append(".*") }) disabledChecks().forEach { disable(it) } - errorChecks().forEach { error(it) } nullaway { - if (name.contains("Test")) { - disable() - } annotatedPackages.add("com.github.benmanes.caffeine") annotatedPackages.add("com.google.common") handleTestAssertionLibraries = true @@ -74,14 +70,12 @@ tasks.withType().configureEach { suggestSuppressions = true checkContracts = true isJSpecifyMode = true + error() } } } } -fun errorChecks() = listOf( - "NullAway", -) fun disabledChecks() = listOf( "AvoidObjectArrays", "AndroidJdkLibsChecker", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0cdabcd31b..c085078342 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-rc-1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/CaffeinatedGuavaTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/CaffeinatedGuavaTest.java index ad5af82de7..f43d044be0 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/CaffeinatedGuavaTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/CaffeinatedGuavaTest.java @@ -105,6 +105,7 @@ void reload_throwable() { } @Test + @SuppressWarnings("NullAway") void cacheLoader_null() { assertThrows(NullPointerException.class, () -> CaffeinatedGuava.caffeinate(null)); diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/GuavaMapTests.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/GuavaMapTests.java index c4c550d06a..196322729a 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/GuavaMapTests.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/GuavaMapTests.java @@ -45,6 +45,7 @@ private static void addGuavaViewTests(TestSuite suite) { return cache.asMap(); }))); suite.addTest(MapTestFactory.suite("GuavaLoadingView", synchronousGenerator(() -> { + @SuppressWarnings("NullAway") Cache cache = CaffeinatedGuava.build( Caffeine.newBuilder().maximumSize(Long.MAX_VALUE), key -> null); return cache.asMap(); diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderFactory.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderFactory.java index 1b0a3a2c49..9a8bc53b8d 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderFactory.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderFactory.java @@ -14,10 +14,13 @@ package com.github.benmanes.caffeine.guava.compatibility; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.base.Function; import com.google.common.base.MoreObjects; @@ -37,17 +40,18 @@ * * @author mike nonemacher */ +@NullUnmarked class CacheBuilderFactory { // Default values contain only 'null', which means don't call the CacheBuilder method (just give // the CacheBuilder default). - private Set concurrencyLevels = Sets.newHashSet((Integer) null); - private Set initialCapacities = Sets.newHashSet((Integer) null); - private Set maximumSizes = Sets.newHashSet((Integer) null); - private Set expireAfterWrites = Sets.newHashSet((DurationSpec) null); - private Set expireAfterAccesses = Sets.newHashSet((DurationSpec) null); - private Set refreshes = Sets.newHashSet((DurationSpec) null); - private Set keyStrengths = Sets.newHashSet((Strength) null); - private Set valueStrengths = Sets.newHashSet((Strength) null); + private Set concurrencyLevels = Collections.singleton(null); + private Set initialCapacities = Collections.singleton(null); + private Set maximumSizes = Collections.singleton(null); + private Set expireAfterWrites = Collections.singleton(null); + private Set expireAfterAccesses = Collections.singleton(null); + private Set refreshes = Collections.singleton(null); + private Set keyStrengths = Collections.singleton(null); + private Set valueStrengths = Collections.singleton(null); @CanIgnoreReturnValue CacheBuilderFactory withConcurrencyLevels(Set concurrencyLevels) { diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderGwtTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderGwtTest.java index 511c992b81..c2f7215dd3 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderGwtTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderGwtTest.java @@ -25,6 +25,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; @@ -49,6 +51,7 @@ * * @author Jon Donovan */ +@NullUnmarked @GwtCompatible @SuppressWarnings("PreferJavaTimeOverload") public class CacheBuilderGwtTest extends TestCase { diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderTest.java index c2ff09e997..3b81777439 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheBuilderTest.java @@ -40,6 +40,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalListener; import com.github.benmanes.caffeine.cache.Ticker; @@ -64,6 +66,7 @@ /** * Unit tests for Caffeine. */ +@NullUnmarked @GwtCompatible(emulated = true) @SuppressWarnings({"CacheLoaderNull", "CanonicalDuration", "PreferJavaTimeOverload", "ThreadPriorityCheck"}) diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheExpirationTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheExpirationTest.java index 641e291da7..7d2d7ad7c2 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheExpirationTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheExpirationTest.java @@ -25,6 +25,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; @@ -46,6 +48,7 @@ * * @author mike nonemacher */ +@NullUnmarked @SuppressWarnings("PreferJavaTimeOverload") public class CacheExpirationTest extends TestCase { diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheLoadingTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheLoadingTest.java index a54848a257..959f726311 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheLoadingTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheLoadingTest.java @@ -39,6 +39,8 @@ import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.logging.Logger; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.github.benmanes.caffeine.guava.compatibility.TestingCacheLoaders.IdentityLoader; @@ -69,6 +71,7 @@ * * @author mike nonemacher */ +@NullUnmarked @SuppressWarnings({"CacheLoaderNull", "PreferJavaTimeOverload", "ThreadPriorityCheck"}) public class CacheLoadingTest extends TestCase { static final Logger logger = Logger.getLogger( diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheReferencesTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheReferencesTest.java index 3c9577afe5..853fc01590 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheReferencesTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/CacheReferencesTest.java @@ -20,6 +20,8 @@ import java.lang.ref.WeakReference; +import org.jspecify.annotations.Nullable; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.github.benmanes.caffeine.guava.compatibility.CacheBuilderFactory.Strength; @@ -127,7 +129,7 @@ public void testInvalidate() { // a strong reference to that value. static final class Key { private final int value; - private WeakReference toString; + private @Nullable WeakReference toString; Key(int value) { this.value = value; diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/EmptyCachesTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/EmptyCachesTest.java index 0e729d09d9..cfefd324e4 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/EmptyCachesTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/EmptyCachesTest.java @@ -26,6 +26,8 @@ import java.util.Set; import java.util.concurrent.ExecutionException; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.github.benmanes.caffeine.guava.compatibility.CacheBuilderFactory.DurationSpec; @@ -45,6 +47,7 @@ * * @author mike nonemacher */ +@NullUnmarked public class EmptyCachesTest extends TestCase { public void testEmpty() { diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalCacheMapComputeTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalCacheMapComputeTest.java index 38120cee77..c6247c86ec 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalCacheMapComputeTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalCacheMapComputeTest.java @@ -22,6 +22,8 @@ import java.util.function.IntConsumer; import java.util.stream.IntStream; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.google.common.cache.Cache; @@ -32,6 +34,7 @@ /** * Test Java8 map.compute in concurrent cache context. */ +@NullUnmarked @SuppressWarnings("PreferJavaTimeOverload") public class LocalCacheMapComputeTest extends TestCase { final int count = 10000; diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalLoadingCacheTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalLoadingCacheTest.java index f2025663f9..4e4a8e5a18 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalLoadingCacheTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/LocalLoadingCacheTest.java @@ -25,6 +25,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.google.common.cache.CacheLoader; @@ -41,6 +43,7 @@ /** * @author Charles Fry */ +@NullUnmarked @SuppressWarnings("JUnit3FloatingPointComparisonWithoutDelta") public class LocalLoadingCacheTest extends TestCase { static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0); diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/NullCacheTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/NullCacheTest.java index 5a683c8c33..50341c3bad 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/NullCacheTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/NullCacheTest.java @@ -22,6 +22,8 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.assertThrows; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.github.benmanes.caffeine.guava.compatibility.TestingRemovalListeners.QueuingRemovalListener; @@ -39,6 +41,7 @@ * * @author mike nonemacher */ +@NullUnmarked @SuppressWarnings("PreferJavaTimeOverload") public class NullCacheTest extends TestCase { QueuingRemovalListener listener; diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/PopulatedCachesTest.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/PopulatedCachesTest.java index 9fef4d0d7e..04cc386861 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/PopulatedCachesTest.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/PopulatedCachesTest.java @@ -28,6 +28,8 @@ import java.util.Map; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.guava.CaffeinatedGuava; import com.github.benmanes.caffeine.guava.compatibility.CacheBuilderFactory.DurationSpec; @@ -50,6 +52,7 @@ * * @author mike nonemacher */ +@NullUnmarked @SuppressWarnings("CollectionToArray") public class PopulatedCachesTest extends TestCase { // we use integers as keys; make sure the range covers some values that ARE cached by diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingCacheLoaders.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingCacheLoaders.java index a68bb3307d..fa41044fea 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingCacheLoaders.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingCacheLoaders.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; import com.google.common.annotations.GwtCompatible; @@ -35,6 +36,7 @@ * * @author mike nonemacher */ +@NullUnmarked @GwtCompatible(emulated = true) public class TestingCacheLoaders { diff --git a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingRemovalListeners.java b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingRemovalListeners.java index a8b3cbda8e..d4ffa8c583 100644 --- a/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingRemovalListeners.java +++ b/guava/src/test/java/com/github/benmanes/caffeine/guava/compatibility/TestingRemovalListeners.java @@ -17,6 +17,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullUnmarked; + import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; import com.google.common.annotations.GwtCompatible; @@ -28,6 +30,7 @@ * * @author mike nonemacher */ +@NullUnmarked @GwtCompatible(emulated = true) class TestingRemovalListeners { diff --git a/jcache/src/main/java/com/github/benmanes/caffeine/jcache/configuration/CaffeineConfiguration.java b/jcache/src/main/java/com/github/benmanes/caffeine/jcache/configuration/CaffeineConfiguration.java index 39aee128ee..d17c8b4807 100644 --- a/jcache/src/main/java/com/github/benmanes/caffeine/jcache/configuration/CaffeineConfiguration.java +++ b/jcache/src/main/java/com/github/benmanes/caffeine/jcache/configuration/CaffeineConfiguration.java @@ -184,7 +184,7 @@ public CaffeineConfiguration removeCacheEntryListenerConfiguration( @CanIgnoreReturnValue @SuppressWarnings("PMD.LinguisticNaming") public CaffeineConfiguration setCacheLoaderFactory( - Factory> factory) { + @Nullable Factory> factory) { checkIfReadOnly(); delegate.setCacheLoaderFactory(factory); return this; @@ -215,7 +215,7 @@ public boolean hasCacheWriter() { @CanIgnoreReturnValue @SuppressWarnings("PMD.LinguisticNaming") public CaffeineConfiguration setCacheWriterFactory( - Factory> factory) { + @Nullable Factory> factory) { checkIfReadOnly(); delegate.setCacheWriterFactory(factory); return this; @@ -230,7 +230,7 @@ public Factory getExpiryPolicyFactory() { @CanIgnoreReturnValue @SuppressWarnings("PMD.LinguisticNaming") public CaffeineConfiguration setExpiryPolicyFactory( - Factory factory) { + @Nullable Factory factory) { checkIfReadOnly(); delegate.setExpiryPolicyFactory(factory); return this; diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/AbstractJCacheTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/AbstractJCacheTest.java index 59be4f1c5c..6d5a3ec8ba 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/AbstractJCacheTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/AbstractJCacheTest.java @@ -16,7 +16,6 @@ package com.github.benmanes.caffeine.jcache; import java.time.Duration; -import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import javax.cache.CacheManager; @@ -38,6 +37,7 @@ import com.google.common.collect.Maps; import com.google.common.testing.FakeTicker; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.uber.nullaway.annotations.Initializer; /** * A testing harness for simplifying the unit tests. @@ -97,6 +97,7 @@ public void afterClass() { } /** The base configuration used by the test. */ + @Initializer protected abstract CaffeineConfiguration getConfiguration(); /* --------------- Utility methods ------------- */ @@ -134,8 +135,8 @@ protected CacheLoader getCacheLoader() { @Override public Integer load(Integer key) { return key; } - @Override public Map loadAll(Iterable keys) { - return Maps.asMap(ImmutableSet.copyOf(keys), this::load); + @Override public ImmutableMap loadAll(Iterable keys) { + return Maps.toMap(ImmutableSet.copyOf(keys), this::load); } }; } diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/CacheProxyTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/CacheProxyTest.java index 4fb48b4bcd..15680c6142 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/CacheProxyTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/CacheProxyTest.java @@ -76,7 +76,7 @@ protected CaffeineConfiguration getLoadingConfiguration() { } @Test - @SuppressWarnings({"ObjectToString", "unchecked"}) + @SuppressWarnings({"NullAway", "ObjectToString", "unchecked"}) public void getConfiguration_immutable() { var config = jcache.getConfiguration(CaffeineConfiguration.class); var type = UnsupportedOperationException.class; diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TestCacheLoader.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TestCacheLoader.java index 613fe9d947..83441bb024 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TestCacheLoader.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TestCacheLoader.java @@ -19,9 +19,12 @@ import javax.cache.integration.CacheLoader; +import org.jspecify.annotations.NullUnmarked; + /** * @author ben.manes@gmail.com (Ben Manes) */ +@NullUnmarked public final class TestCacheLoader implements CacheLoader { @Override public Integer load(Integer key) { return null; diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TypesafeConfigurationTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TypesafeConfigurationTest.java index dceeda997a..b4f533827f 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TypesafeConfigurationTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/configuration/TypesafeConfigurationTest.java @@ -17,6 +17,7 @@ import static com.github.benmanes.caffeine.jcache.configuration.TypesafeConfigurator.configSource; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertThrows; import java.net.URI; @@ -274,8 +275,9 @@ static void checkTestCache(CaffeineConfiguration config) { assertThat(config.getValueType()).isEqualTo(Object.class); assertThat(config.getExecutorFactory().create()).isInstanceOf(TestExecutor.class); assertThat(config.getSchedulerFactory().create()).isInstanceOf(TestScheduler.class); - assertThat(config.getCacheLoaderFactory().create()).isInstanceOf(TestCacheLoader.class); assertThat(config.getCacheWriter()).isInstanceOf(TestCacheWriter.class); + assertThat(requireNonNull(config.getCacheLoaderFactory()).create()) + .isInstanceOf(TestCacheLoader.class); assertThat(config.isNativeStatisticsEnabled()).isTrue(); assertThat(config.isStatisticsEnabled()).isTrue(); assertThat(config.isManagementEnabled()).isTrue(); @@ -292,7 +294,8 @@ static void checkStoreByValue(CaffeineConfiguration config) { } static void checkListener(CompleteConfiguration config) { - var listener = Iterables.getOnlyElement(config.getCacheEntryListenerConfigurations()); + var listener = requireNonNull(Iterables.getOnlyElement( + config.getCacheEntryListenerConfigurations())); assertThat(listener.getCacheEntryListenerFactory().create()) .isInstanceOf(TestCacheEntryListener.class); assertThat(listener.getCacheEntryEventFilterFactory().create()) diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/copy/JavaSerializationCopierTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/copy/JavaSerializationCopierTest.java index 9af31ba263..c8a940e925 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/copy/JavaSerializationCopierTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/copy/JavaSerializationCopierTest.java @@ -54,11 +54,13 @@ public void constructor_null(Set> immutableClasses, new JavaSerializationCopier(immutableClasses, deepCopyStrategies)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "copier") public void null_object(Copier copier) { assertThrows(NullPointerException.class, () -> copy(copier, null)); } + @SuppressWarnings("NullAway") @Test(dataProvider = "copier") public void null_classLoader(Copier copier) { assertThrows(NullPointerException.class, () -> copier.copy(1, null)); diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventDispatcherTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventDispatcherTest.java index d277676bd0..aab392fbc4 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventDispatcherTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventDispatcherTest.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.jcache.event; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import static javax.cache.event.EventType.CREATED; import static javax.cache.event.EventType.EXPIRED; import static javax.cache.event.EventType.REMOVED; @@ -24,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.quality.Strictness.STRICT_STUBS; import java.util.ArrayList; import java.util.List; @@ -45,9 +47,10 @@ import javax.cache.event.CacheEntryUpdatedListener; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.testng.MockitoSettings; +import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.google.common.collect.Iterables; @@ -57,6 +60,9 @@ /** * @author ben.manes@gmail.com (Ben Manes) */ +@Test(singleThreaded = true) +@Listeners(MockitoTestNGListener.class) +@MockitoSettings(strictness = STRICT_STUBS) public final class EventDispatcherTest { @Mock Cache cache; @Mock CacheEntryCreatedListener createdListener; @@ -69,11 +75,6 @@ public final class EventDispatcherTest { CacheEntryEventFilter allowFilter = event -> true; CacheEntryEventFilter rejectFilter = event -> false; - @BeforeMethod - public void beforeMethod() throws Exception { - MockitoAnnotations.openMocks(this).close(); - } - @AfterTest public void afterTest() { executorService.shutdownNow(); @@ -245,7 +246,7 @@ public void parallel_keys() { task.run(); }); CacheEntryCreatedListener listener = events -> { - var event = Iterables.getOnlyElement(events); + var event = requireNonNull(Iterables.getOnlyElement(events)); if (event.getKey().equals(1)) { received1.set(true); await().untilTrue(run1); diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeAwareListenerTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeAwareListenerTest.java index 55174b595f..bf200bab5d 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeAwareListenerTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeAwareListenerTest.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.STRICT_STUBS; import java.io.IOException; @@ -32,23 +33,22 @@ import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.testng.annotations.BeforeMethod; +import org.mockito.testng.MockitoSettings; +import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; import org.testng.annotations.Test; /** * @author ben.manes@gmail.com (Ben Manes) */ +@Test(singleThreaded = true) @SuppressWarnings("unchecked") +@Listeners(MockitoTestNGListener.class) +@MockitoSettings(strictness = STRICT_STUBS) public final class EventTypeAwareListenerTest { @Mock Cache cache; - @BeforeMethod - public void before() throws Exception { - MockitoAnnotations.openMocks(this).close(); - } - @Test public void closed() throws IOException { when(cache.isClosed()).thenReturn(true); diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeFilterTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeFilterTest.java index 1cc67e0504..4a6b6e0a8a 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeFilterTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/EventTypeFilterTest.java @@ -19,7 +19,7 @@ import javax.cache.event.CacheEntryCreatedListener; -import org.testng.annotations.BeforeTest; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** @@ -28,7 +28,7 @@ public final class EventTypeFilterTest { EventTypeFilter filter; - @BeforeTest + @BeforeClass public void before() { CacheEntryCreatedListener created = events -> {}; filter = new EventTypeFilter<>(created, event -> true); diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEntryEventTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEntryEventTest.java index 3112441b48..2063ee9697 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEntryEventTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEntryEventTest.java @@ -17,6 +17,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.mockito.quality.Strictness.STRICT_STUBS; import java.util.Iterator; import java.util.Map; @@ -26,8 +27,10 @@ import javax.cache.event.EventType; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.testng.annotations.BeforeTest; +import org.mockito.testng.MockitoSettings; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.google.common.collect.testing.IteratorFeature; @@ -36,14 +39,16 @@ /** * @author ben.manes@gmail.com (Ben Manes) */ +@Test(singleThreaded = true) +@Listeners(MockitoTestNGListener.class) +@MockitoSettings(strictness = STRICT_STUBS) public final class JCacheEntryEventTest { @Mock Cache cache; JCacheEntryEvent event; - @BeforeTest - public void before() throws Exception { - MockitoAnnotations.openMocks(this).close(); + @BeforeMethod + public void before() { event = new JCacheEntryEvent<>(cache, EventType.CREATED, 1, /* hasOldValue= */ true, 2, 3); } diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEvictionListenerTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEvictionListenerTest.java index 174ddf9c75..44db10fc59 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEvictionListenerTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/event/JCacheEvictionListenerTest.java @@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.quality.Strictness.STRICT_STUBS; import java.util.Arrays; import java.util.Iterator; @@ -29,9 +30,11 @@ import javax.cache.event.CacheEntryRemovedListener; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.testng.MockitoSettings; +import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.RemovalCause; @@ -41,6 +44,9 @@ /** * @author ben.manes@gmail.com (Ben Manes) */ +@Test(singleThreaded = true) +@Listeners(MockitoTestNGListener.class) +@MockitoSettings(strictness = STRICT_STUBS) public final class JCacheEvictionListenerTest { JCacheEvictionListener listener; JCacheStatisticsMXBean statistics; @@ -49,8 +55,7 @@ public final class JCacheEvictionListenerTest { @Mock Cache cache; @BeforeMethod - public void before() throws Exception { - MockitoAnnotations.openMocks(this).close(); + public void before() { statistics = new JCacheStatisticsMXBean(); var dispatcher = new EventDispatcher(Runnable::run); listener = new JCacheEvictionListener<>(dispatcher, statistics); diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheAccessExpiryTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheAccessExpiryTest.java index 25b3ebbd14..7d987b0842 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheAccessExpiryTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheAccessExpiryTest.java @@ -99,6 +99,7 @@ public void get_absent() { @Test(dataProvider = "eternal") public void get_present(boolean eternal) { var expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); if (eternal) { expirable.setExpireTimeMillis(Long.MAX_VALUE); } @@ -127,6 +128,7 @@ public void get_loading_absent() { assertThat(jcacheLoading.get(KEY_1)).isEqualTo(KEY_1); var expirable = getExpirable(jcacheLoading, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -134,11 +136,13 @@ public void get_loading_absent() { @Test(dataProvider = "eternal") public void get_loading_present(boolean eternal) { var expirable = getExpirable(jcacheLoading, KEY_1); + assertThat(expirable).isNotNull(); if (eternal) { expirable.setExpireTimeMillis(Long.MAX_VALUE); } assertThat(jcacheLoading.get(KEY_1)).isEqualTo(VALUE_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -161,6 +165,7 @@ public void getAll_absent() { public void getAll_present(boolean eternal) { for (Integer key : keys) { var expirable = getExpirable(jcacheLoading, key); + assertThat(expirable).isNotNull(); if (eternal) { expirable.setExpireTimeMillis(Long.MAX_VALUE); } @@ -170,6 +175,7 @@ public void getAll_present(boolean eternal) { for (Integer key : keys) { var expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -191,6 +197,7 @@ public void invoke_absent() { @Test(dataProvider = "eternal") public void invoke_present(boolean eternal) { var expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); if (eternal) { expirable.setExpireTimeMillis(Long.MAX_VALUE); } @@ -223,6 +230,7 @@ public void invokeAll_present() { for (Integer key : keys) { var expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -235,6 +243,7 @@ public void removeConditionally() { assertThat(jcache.remove(KEY_1, VALUE_2)).isFalse(); var expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -246,6 +255,7 @@ public void replaceConditionally() { assertThat(jcache.replace(KEY_1, VALUE_2, VALUE_3)).isFalse(); var expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCombinedExpiryTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCombinedExpiryTest.java index b773fdcdfa..01ede2ea13 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCombinedExpiryTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCombinedExpiryTest.java @@ -100,6 +100,7 @@ public void put_expired() { jcache.put(KEY_1, VALUE_2); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -113,6 +114,7 @@ public void putIfAbsent_expired() { assertThat(jcache.putIfAbsent(KEY_1, VALUE_2)).isTrue(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.get()).isEqualTo(VALUE_2); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCreationExpiryTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCreationExpiryTest.java index 5980bd839d..01dee9d3be 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCreationExpiryTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheCreationExpiryTest.java @@ -88,6 +88,7 @@ public void get_expired() { public void get_loading_absent() { assertThat(jcacheLoading.get(KEY_1)).isEqualTo(KEY_1); Expirable expirable = getExpirable(jcacheLoading, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -99,6 +100,7 @@ public void get_loading_expired() { assertThat(jcacheLoading.get(KEY_1)).isEqualTo(KEY_1); Expirable expirable = getExpirable(jcacheLoading, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -120,7 +122,9 @@ public void get_loading_present() { assertThat(jcacheLoading.get(KEY_1)).isEqualTo(VALUE_1); Expirable expirable = getExpirable(jcacheLoading, KEY_1); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } /* --------------- getAndPut --------------- */ @@ -130,6 +134,7 @@ public void getAndPut_absent() { assertThat(jcache.getAndPut(KEY_1, VALUE_1)).isNull(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -141,6 +146,7 @@ public void getAndPut_expired() { assertThat(jcache.getAndPut(KEY_1, VALUE_1)).isNull(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -152,7 +158,9 @@ public void getAndPut_present() { assertThat(jcache.getAndPut(KEY_1, VALUE_2)).isEqualTo(VALUE_1); Expirable expirable = getExpirable(jcache, KEY_1); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } /* --------------- put --------------- */ @@ -162,6 +170,7 @@ public void put_absent() { jcache.put(KEY_1, VALUE_1); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -173,6 +182,7 @@ public void put_expired() { jcache.put(KEY_1, VALUE_2); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -184,7 +194,9 @@ public void put_present() { jcache.put(KEY_1, VALUE_2); Expirable expirable = getExpirable(jcache, KEY_1); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } /* --------------- putAll --------------- */ @@ -195,6 +207,7 @@ public void putAll_absent() { for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -208,6 +221,7 @@ public void putAll_expired() { jcache.putAll(entries); for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -221,7 +235,9 @@ public void putAll_present() { jcache.putAll(entries); for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } } @@ -232,6 +248,7 @@ public void putIfAbsent_absent() { jcache.putIfAbsent(KEY_1, VALUE_1); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -243,6 +260,7 @@ public void putIfAbsent_expired() { assertThat(jcache.putIfAbsent(KEY_1, VALUE_2)).isTrue(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.get()).isEqualTo(VALUE_2); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); @@ -263,7 +281,9 @@ public void putIfAbsent_present() { jcache.putIfAbsent(KEY_1, VALUE_2); Expirable expirable = getExpirable(jcache, KEY_1); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } /* --------------- invoke --------------- */ @@ -277,6 +297,7 @@ public void invoke_absent() { assertThat(result).isNull(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -293,6 +314,7 @@ public void invoke_expired() { assertThat(result).isNull(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -309,7 +331,9 @@ public void invoke_present() { assertThat(result).isNull(); Expirable expirable = getExpirable(jcache, KEY_1); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } /* --------------- invokeAll --------------- */ @@ -324,6 +348,7 @@ public void invokeAll_absent() { for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -342,6 +367,7 @@ public void invokeAll_expired() { for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -360,6 +386,7 @@ public void invokeAll_present() { for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheUpdateExpiryTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheUpdateExpiryTest.java index 0a8808b8d4..ee1cd93f07 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheUpdateExpiryTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/expiry/JCacheUpdateExpiryTest.java @@ -85,6 +85,7 @@ public void getAndPut() { assertThat(value).isEqualTo(VALUE_1); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -96,6 +97,7 @@ public void getAndReplace() { assertThat(jcache.getAndReplace(KEY_1, VALUE_2)).isEqualTo(VALUE_1); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -107,6 +109,7 @@ public void put() { jcache.put(KEY_1, VALUE_2); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis() ).isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -119,6 +122,7 @@ public void putAll() { jcache.putAll(entries); for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -131,6 +135,7 @@ public void replace() { jcache.replace(KEY_1, VALUE_2); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -142,6 +147,7 @@ public void replaceConditionally() { assertThat(jcache.replace(KEY_1, VALUE_1, VALUE_2)).isTrue(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -153,7 +159,9 @@ public void replaceConditionally_failed() { assertThat(jcache.replace(KEY_1, VALUE_2, VALUE_3)).isFalse(); Expirable expirable = getExpirable(jcache, KEY_1); - assertThat(expirable.getExpireTimeMillis()).isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); + assertThat(expirable).isNotNull(); + assertThat(expirable.getExpireTimeMillis()) + .isEqualTo(START_TIME.plus(EXPIRY_DURATION).toMillis()); } @Test @@ -168,6 +176,7 @@ public void invoke() { assertThat(result).isNull(); Expirable expirable = getExpirable(jcache, KEY_1); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } @@ -185,6 +194,7 @@ public void invokeAll() { for (Integer key : keys) { Expirable expirable = getExpirable(jcache, key); + assertThat(expirable).isNotNull(); assertThat(expirable.getExpireTimeMillis()) .isEqualTo(currentTime().plus(EXPIRY_DURATION).toMillis()); } diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/processor/EntryProcessorTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/processor/EntryProcessorTest.java index 0acf5843d3..ac9bf03a1f 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/processor/EntryProcessorTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/processor/EntryProcessorTest.java @@ -33,6 +33,7 @@ import javax.cache.integration.CacheWriter; import javax.cache.processor.MutableEntry; +import org.jspecify.annotations.Nullable; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -104,7 +105,7 @@ public void writeOccursForInitialLoadOfEntry() { assertThat(value).isNull(); } - private static Object process(MutableEntry entry) { + private static @Nullable Object process(MutableEntry entry) { var value = 1 + firstNonNull(entry.getValue(), 0); entry.setValue(value); return null; @@ -135,7 +136,7 @@ public void deleteAll(Collection keys) { } final class MapLoader implements CacheLoader { - @Override public Integer load(Integer key) { + @Override public @Nullable Integer load(Integer key) { loads++; return map.get(key); } diff --git a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/size/JCacheMaximumWeightTest.java b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/size/JCacheMaximumWeightTest.java index 2497598b91..a2a6a5495c 100644 --- a/jcache/src/test/java/com/github/benmanes/caffeine/jcache/size/JCacheMaximumWeightTest.java +++ b/jcache/src/test/java/com/github/benmanes/caffeine/jcache/size/JCacheMaximumWeightTest.java @@ -16,6 +16,7 @@ package com.github.benmanes.caffeine.jcache.size; import static com.google.common.truth.Truth.assertThat; +import static java.util.Objects.requireNonNull; import java.util.Optional; import java.util.OptionalLong; @@ -46,7 +47,7 @@ public final class JCacheMaximumWeightTest extends AbstractJCacheTest { @Override protected CaffeineConfiguration getConfiguration() { CacheEntryRemovedListener listener = events -> - removedWeight.addAndGet(Iterables.getOnlyElement(events).getValue()); + removedWeight.addAndGet(requireNonNull(Iterables.getOnlyElement(events)).getValue()); var configuration = new CaffeineConfiguration(); configuration.setMaximumWeight(OptionalLong.of(MAXIMUM)); configuration.setWeigherFactory(Optional.of(() -> (key, value) -> value)); diff --git a/qodana.yaml b/qodana.yaml index 9f15e6adb1..b6692fc3f2 100644 --- a/qodana.yaml +++ b/qodana.yaml @@ -1,6 +1,6 @@ version: "1.0" projectJDK: "21" -linter: jetbrains/qodana-jvm-community:2024.2 +linter: jetbrains/qodana-jvm-community:2024.3 profile: name: qodana.recommended licenseRules: diff --git a/settings.gradle.kts b/settings.gradle.kts index cab1d2d545..451101b3ab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,9 +2,9 @@ pluginManagement { includeBuild("gradle/plugins") } plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.2" - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } dependencyResolutionManagement {